BOT/.idea/.name
____________________________________________
mental_automatizm_INFO_functions_Purpose.go
____________________________________________

BOT/.idea/BOT.iml
____________________________________________
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
  <component name="Go" enabled="true" />
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$" />
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>
____________________________________________

BOT/.idea/misc.xml
____________________________________________
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="JavaScriptSettings">
    <option name="languageLevel" value="ES6" />
  </component>
</project>
____________________________________________

BOT/.idea/modules.xml
____________________________________________
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/.idea/BOT.iml" filepath="$PROJECT_DIR$/.idea/BOT.iml" />
    </modules>
  </component>
</project>
____________________________________________

BOT/.idea/SAVE_FOR CORRUPT_workspace.xml
____________________________________________
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="BranchesTreeState">
    <expand>
      <path>
        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="LOCAL_ROOT" type="e8cecc67:BranchNodeDescriptor" />
      </path>
      <path>
        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
      </path>
      <path>
        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="GROUP_NODE:origin" type="e8cecc67:BranchNodeDescriptor" />
      </path>
    </expand>
    <select />
  </component>
  <component name="ChangeListManager">
    <list default="true" id="45b93535-895e-4266-854f-483f30922cea" name="Default Changelist" comment="тест (коммент)">
      <change beforePath="$PROJECT_DIR$/brain/psychic/PultGetSelfPerceptionInfo.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/PultGetSelfPerceptionInfo.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/automatizm_functions.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/automatizm_functions.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/base_state_image.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/base_state_image.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/mental_automatism_tree_pult_show.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/mental_automatism_tree_pult_show.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/mental_automatizm_INFO_functions.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/mental_automatizm_INFO_functions.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/mental_automatizm_INFO_functions_Purpose.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/mental_automatizm_INFO_functions_Purpose.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/understanding.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/understanding.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/understanding_for_pult.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/understanding_for_pult.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/understanding_problem_tree_pult.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/understanding_problem_tree_pult.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/understanding_theme_image.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/understanding_theme_image.go" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/brain/psychic/understanding_tree.go" beforeDir="false" afterPath="$PROJECT_DIR$/brain/psychic/understanding_tree.go" afterDir="false" />
    </list>
    <option name="SHOW_DIALOG" value="false" />
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
    <option name="LAST_RESOLUTION" value="IGNORE" />
  </component>
  <component name="ComposerSettings">
    <execution />
  </component>
  <component name="FileTemplateManagerImpl">
    <option name="RECENT_TEMPLATES">
      <list>
        <option value="Go Application" />
        <option value="Go File" />
      </list>
    </option>
  </component>
  <component name="GOROOT" path="C:\Go" />
  <component name="Git.Settings">
    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
  </component>
  <component name="GoLibraries">
    <option name="indexEntireGoPath" value="false" />
  </component>
  <component name="ProjectId" id="27mnvvRzUGTsVvBhjE3aBobAyEP" />
  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
  <component name="ProjectViewState">
    <option name="hideEmptyMiddlePackages" value="true" />
    <option name="showLibraryContents" value="true" />
  </component>
  <component name="PropertiesComponent">
    <property name="DefaultGoTemplateProperty" value="Go File" />
    <property name="GO_FMT" value="true" />
    <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
    <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
    <property name="WebServerToolWindowFactoryState" value="false" />
    <property name="go.import.settings.migrated" value="true" />
    <property name="last_opened_file_path" value="A:/OSPanel/domains/scorcher" />
    <property name="settings.editor.selected.configurable" value="org.jetbrains.plugins.github.ui.GithubSettingsConfigurable" />
    <property name="vue.rearranger.settings.migration" value="true" />
  </component>
  <component name="RunManager" selected="Go Build.go build main.go">
    <configuration name="go build BOT" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
      <module name="BOT" />
      <working_directory value="$PROJECT_DIR$/" />
      <kind value="PACKAGE" />
      <filePath value="$PROJECT_DIR$/main.go" />
      <package value="BOT" />
      <directory value="$PROJECT_DIR$/" />
      <output_directory value="A:/OSPanel/domains/go/" />
      <method v="2" />
    </configuration>
    <configuration name="go build main.go" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
      <module name="BOT" />
      <working_directory value="$PROJECT_DIR$/" />
      <kind value="FILE" />
      <filePath value="$PROJECT_DIR$/main.go" />
      <package value="BOT" />
      <directory value="$PROJECT_DIR$/" />
      <output_directory value="A:/OSPanel/domains/go/" />
      <method v="2" />
    </configuration>
    <list>
      <item itemvalue="Go Build.go build BOT" />
      <item itemvalue="Go Build.go build main.go" />
    </list>
    <recent_temporary>
      <list>
        <item itemvalue="Go Build.go build main.go" />
        <item itemvalue="Go Build.go build BOT" />
      </list>
    </recent_temporary>
  </component>
  <component name="SpellCheckerSettings" BundledDictionaries="0" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" Dictionaries="0" CorrectionsLimit="5" DefaultDictionary="application-level" UseSingleDictionary="true" />
  <component name="TaskManager">
    <task active="true" id="Default" summary="Default task">
      <created>1659519944390</created>
      <option name="number" value="Default" />
      <option name="presentableId" value="Default" />
      <updated>1659519944390</updated>
      <workItem from="1659519952728" duration="6000" />
      <workItem from="1659608979020" duration="13000" />
      <workItem from="1659705502614" duration="45000" />
    </task>
    <servers />
  </component>
  <component name="TypeScriptGeneratedFilesManager">
    <option name="version" value="2" />
  </component>
  <component name="Vcs.Log.Tabs.Properties">
    <option name="TAB_STATES">
      <map>
        <entry key="MAIN">
          <value>
            <State />
          </value>
        </entry>
      </map>
    </option>
    <option name="oldMeFiltersMigrated" value="true" />
  </component>
  <component name="VcsManagerConfiguration">
    <MESSAGE value="тест (коммент)" />
    <option name="LAST_COMMIT_MESSAGE" value="тест (коммент)" />
  </component>
  <component name="WindowStateProjectService">
    <state x="464" y="168" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1679634008549">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state x="464" y="168" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679634008549" />
    <state width="789" height="466" key="DebuggerActiveHint" timestamp="1679917173067">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="789" height="466" key="DebuggerActiveHint/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917173067" />
    <state width="1741" height="269" key="GridCell.Tab.0.bottom" timestamp="1679917517646">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.0.bottom/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517646" />
    <state width="1741" height="269" key="GridCell.Tab.0.center" timestamp="1679917517646">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.0.center/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517646" />
    <state width="1741" height="269" key="GridCell.Tab.0.left" timestamp="1679917517645">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.0.left/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517645" />
    <state width="1741" height="269" key="GridCell.Tab.0.right" timestamp="1679917517646">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.0.right/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517646" />
    <state width="1741" height="269" key="GridCell.Tab.1.bottom" timestamp="1679917517647">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.1.bottom/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517647" />
    <state width="1741" height="269" key="GridCell.Tab.1.center" timestamp="1679917517646">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.1.center/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517646" />
    <state width="1741" height="269" key="GridCell.Tab.1.left" timestamp="1679917517646">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.1.left/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517646" />
    <state width="1741" height="269" key="GridCell.Tab.1.right" timestamp="1679917517647">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="1741" height="269" key="GridCell.Tab.1.right/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679917517647" />
    <state width="862" height="509" key="XDebugger.FullValuePopup" timestamp="1679910479368">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state width="862" height="509" key="XDebugger.FullValuePopup/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679910479368" />
    <state x="2002" y="61" width="1432" height="906" key="dock-window-1" timestamp="1679479845510">
      <screen x="1920" y="0" width="1920" height="1019" />
    </state>
    <state x="2002" y="61" width="1432" height="906" key="dock-window-1/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679479845510" />
    <state x="671" y="223" width="672" height="678" key="search.everywhere.popup" timestamp="1679481034314">
      <screen x="0" y="0" width="1920" height="1019" />
    </state>
    <state x="671" y="223" width="672" height="678" key="search.everywhere.popup/0.0.1920.1019/-1920.0.1920.1019/1920.0.1920.1019@0.0.1920.1019" timestamp="1679481034314" />
  </component>
  <component name="XDebuggerManager">
    <breakpoint-manager>
      <breakpoints>
        <line-breakpoint enabled="true" type="DlvLineBreakpoint">
          <url>file://$PROJECT_DIR$/lib/map_guard.go</url>
          <line>66</line>
          <option name="timeStamp" value="2" />
        </line-breakpoint>
        <line-breakpoint enabled="true" type="DlvLineBreakpoint">
          <url>file://$PROJECT_DIR$/brain/psychic/mental_automatism_tree_pult_show.go</url>
          <line>136</line>
          <option name="timeStamp" value="17" />
        </line-breakpoint>
      </breakpoints>
    </breakpoint-manager>
    <watches-manager>
      <configuration name="GoApplicationRunConfiguration">
        <watch expression="PulsCount" language="go" />
        <watch expression="IsSleeping" language="go" />
        <watch expression="ReadiStatus" language="go" />
        <watch expression="conscienceStatus" language="go" />
        <watch expression="GomeostazParams[1]" language="go" />
        <watch expression="curBrangeArr" language="go" />
        <watch expression="BaseStyleArr" />
        <watch expression="cld.ID" />
        <watch expression="action_sensor.FoodPortionForEnergi" />
      </configuration>
    </watches-manager>
  </component>
</project>
____________________________________________

BOT/.idea/workspace.xml
____________________________________________
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="AutoImportSettings">
    <option name="autoReloadType" value="ALL" />
  </component>
  <component name="BranchesTreeState">
    <expand>
      <path>
        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="LOCAL_ROOT" type="e8cecc67:BranchNodeDescriptor" />
      </path>
      <path>
        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
      </path>
      <path>
        <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
        <item name="GROUP_NODE:origin" type="e8cecc67:BranchNodeDescriptor" />
      </path>
    </expand>
    <select />
  </component>
  <component name="ChangeListManager">
    <list default="true" id="45b93535-895e-4266-854f-483f30922cea" name="Default Changelist" comment="тест (коммент)" />
    <option name="SHOW_DIALOG" value="false" />
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
    <option name="LAST_RESOLUTION" value="IGNORE" />
  </component>
  <component name="ComposerSettings">
    <execution />
  </component>
  <component name="FileTemplateManagerImpl">
    <option name="RECENT_TEMPLATES">
      <list>
        <option value="Go Application" />
        <option value="Go File" />
      </list>
    </option>
  </component>
  <component name="GOROOT" url="file://C:/Program Files/Go" />
  <component name="Git.Settings">
    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
  </component>
  <component name="GoLibraries">
    <option name="indexEntireGoPath" value="false" />
  </component>
  <component name="ProjectId" id="27mnvvRzUGTsVvBhjE3aBobAyEP" />
  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
  <component name="ProjectViewState">
    <option name="hideEmptyMiddlePackages" value="true" />
    <option name="showLibraryContents" value="true" />
  </component>
  <component name="PropertiesComponent">{
  &quot;keyToString&quot;: {
    &quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
    &quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
    &quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
    &quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
    &quot;TODO_SCOPE&quot;: &quot;All Places&quot;,
    &quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
    &quot;go.import.settings.migrated&quot;: &quot;true&quot;,
    &quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
    &quot;last_opened_file_path&quot;: &quot;C:/Users/Пользователь&quot;,
    &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
    &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
    &quot;settings.editor.selected.configurable&quot;: &quot;inlay.hints&quot;
  }
}</component>
  <component name="RunManager" selected="Go Build.go build BOT">
    <configuration name="go build BOT" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
      <module name="BOT" />
      <working_directory value="$PROJECT_DIR$/" />
      <kind value="PACKAGE" />
      <package value="BOT" />
      <directory value="$PROJECT_DIR$/" />
      <filePath value="$PROJECT_DIR$/main.go" />
      <output_directory value="A:/OSPanel/domains/go/" />
      <method v="2" />
    </configuration>
    <configuration name="go build main.go" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true">
      <module name="BOT" />
      <working_directory value="$PROJECT_DIR$/" />
      <kind value="FILE" />
      <package value="BOT" />
      <directory value="$PROJECT_DIR$/" />
      <filePath value="$PROJECT_DIR$/main.go" />
      <output_directory value="A:/OSPanel/domains/go/" />
      <method v="2" />
    </configuration>
    <recent_temporary>
      <list>
        <item itemvalue="Go Build.go build BOT" />
        <item itemvalue="Go Build.go build main.go" />
      </list>
    </recent_temporary>
  </component>
  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
  <component name="TaskManager">
    <task active="true" id="Default" summary="Default task">
      <created>1659519944390</created>
      <option name="number" value="Default" />
      <option name="presentableId" value="Default" />
      <updated>1659519944390</updated>
      <workItem from="1659519952728" duration="6000" />
      <workItem from="1659608979020" duration="13000" />
      <workItem from="1659705502614" duration="45000" />
      <workItem from="1708435673855" duration="304000" />
      <workItem from="1708435982600" duration="25000" />
      <workItem from="1708436026359" duration="184000" />
      <workItem from="1708436224188" duration="7000" />
      <workItem from="1708436309687" duration="159000" />
      <workItem from="1708436497553" duration="1947000" />
    </task>
    <servers />
  </component>
  <component name="TypeScriptGeneratedFilesManager">
    <option name="version" value="3" />
  </component>
  <component name="Vcs.Log.Tabs.Properties">
    <option name="TAB_STATES">
      <map>
        <entry key="MAIN">
          <value>
            <State />
          </value>
        </entry>
      </map>
    </option>
  </component>
  <component name="VcsManagerConfiguration">
    <MESSAGE value="тест (коммент)" />
    <option name="LAST_COMMIT_MESSAGE" value="тест (коммент)" />
  </component>
  <component name="VgoProject">
    <integration-enabled>false</integration-enabled>
    <settings-migrated>true</settings-migrated>
  </component>
  <component name="XDebuggerManager">
    <breakpoint-manager>
      <breakpoints>
        <line-breakpoint enabled="true" type="DlvLineBreakpoint">
          <url>file://$PROJECT_DIR$/brain/words_sensor/phrase_tree.go</url>
          <line>391</line>
          <option name="timeStamp" value="45" />
        </line-breakpoint>
      </breakpoints>
    </breakpoint-manager>
    <watches-manager>
      <configuration name="GoApplicationRunConfiguration">
        <watch expression="isUnrecognizedPhraseFromAtmtzmTreeActivation" language="go" />
        <watch expression="oldDetectedActiveLastProblemNodID" language="go" />
        <watch expression="targetEpisodicStrIdArr" language="go" />
        <watch expression="&amp;EpisodicTree.Children[0].Children[0]" />
        <watch expression="level" />
        <watch expression="info" />
        <watch expression="c0[0]" />
        <watch expression="i" />
        <watch expression="*(*&quot;int&quot;)(7637792)" />
      </configuration>
    </watches-manager>
  </component>
</project>
____________________________________________

BOT/brain/action_sensor/action.go
____________________________________________
/*
Ощущение действий с Пульта
Могут быть совершены сразу несколько действий.
Так что возникает образ действий (а далее - и общий образ действий и фраз)

*/

package action_sensor

import (
	"BOT/brain/gomeostas"
	"BOT/brain/transfer"
	"strconv"
	"strings"
)

// ////////////////////////////////////////////
func init() {

}

/////////////////////////////////////////

var pulsCount = 0
var LifeTime = 0
var EvolushnStage = 0 // стадия развития
var IsStressing = false

////////////////////////////////////////////////////////////////////

// срабатывает с каждым пульсом
func ActionSensorPuls(evolushnStage int, lifeTime int, puls int, isSlipping bool) {
	LifeTime = lifeTime
	EvolushnStage = evolushnStage
	pulsCount = puls
	IsStressing = isSlipping
	CheckCurActions()
}

/*
	список активных действий с Пульта

Могут быть совершены сразу несколько действий, контекст которых удерживаются в течении 10 секунд.
В ActionFromPult сохраняется время активации действия в числе пульсов pulsCount

0 Нет никаких действий с Пульта
1 Непонятно
2 Понятно
3 Наказать
4 Поощрить
5 Накормить
6 Успокоить
7 Предложить поиграть
8 Предложить поучить
9 Игнорировать
10 Сделать больно
11 Сделать приятно
12 Заплакать
13 Засмеяться
14 Обрадоваться
15 Испугаться
16 Простить
17 Вылечить
*/
var ActionFromPult [18]int // время активации действия живет на время активации с пульта до завершения прохода дерева
// сохрянять текущий контекст ActionFromPultContext=ActionFromPult
var ActionFromPultContext [18]int // эти не очищаются

// название Базового контекста из его ID str:=action_sensor.GetActionNameFromID(id)
func GetActionNameFromID(id int) string {
	var out = ""
	switch id {
	case 1:
		out = "Непонятно"
	case 2:
		out = "Понятно"
	case 3:
		out = "Наказать"
	case 4:
		out = "Поощрить"
	case 5:
		out = "Накормить"
	case 6:
		out = "Успокоить"
	case 7:
		out = "Предложить поиграть"
	case 8:
		out = "Предложить поучить"
	case 9:
		out = "Игнорировать"
	case 10:
		out = "Сделать больно"
	case 11:
		out = "Сделать приятно"
	case 12:
		out = "Заплакать"
	case 13:
		out = "Засмеяться"
	case 14:
		out = "Обрадоваться"
	case 15:
		out = "Испугаться"
	case 16:
		out = "Простить"
	case 17:
		out = "Вылечить"
	}
	return out
}

//var IsPress3or4button = false /*нажата кнопка Наказать или Поощрить - пока под вопросом, но используется*/

//////////////////////////////////////////////////////////////

// учесть мотивирующий эффект Настроения сообщения Оператора -
func UpdateMoodEffectFromMessage(moodID int) {
	gomeostas.SetMoodePraseEffectArr(moodID)
}

///////////////////////////////////////////////////////////

/*
	акция с кнопок пульта

сопровождается гомеостатическими эффектами GomeostazActionEffectArr =make(map[int]string)
*/
func SetActionFromPult(actionList string, energi int) {
	// очищать прежние акции с пульта перед установкой новых т.к. сочетания передаются сразу.
	DeactivationTriggers()
	DeactivationTriggersContext()

	actArr := strings.Split(actionList, "|")
	//var listID []int
	for n := 0; n < len(actArr); n++ {
		if len(actArr[n]) == 0 {
			continue
		}
		actionID, _ := strconv.Atoi(actArr[n])
		/*
			при нажатии на кнопки включается и выключается игровой режим.
			Но он НЕ может включаться по инициативе бота
			и поэтому на пульте /pult_actions.php в function to_action(id
			жстко переключения привязаны к кнопкам для индикации игрового режима.

			//Непонятно, поучить, Сделать больно,Сделать приятно,Заплакать,Испугаться,Вылечить
			if actionID == 1 || actionID == 8 || actionID == 10 || actionID == 11  || actionID == 12 || actionID == 15 || actionID == 17{
				transfer.IsPsychicGameMode = false  // Выключить игровой режим
			}*/

		if actionID == 6 { //  Успокоить
			// Снять режимы Плохо и Хорошо
			//			gomeostas.CliarWellBad()  нажатием ESC или по крестику у плашки Плохо-Хорошо
		}

		if actionID == 7 { //  Поиграть
			transfer.IsPsychicGameMode = true // Включить игрового режима
		}

		if actionID == 8 { //  Поучить
			transfer.IsPsychicТeachingMode = true // Включить учителского режима
		}

		if actionID == 5 { //  Накормить
			switch energi {
			case 1:
				gomeostas.ChangeGomeostazParametr(1, 20.0)
			case 2:
				gomeostas.ChangeGomeostazParametr(1, 50.0)
			case 3:
				gomeostas.ChangeGomeostazParametr(1, 80.0)
			}
		} else {
			/*
				if actionID == 3 || actionID == 4 {
					IsPress3or4button = true // нажата кнопка Наказать или Поощрить
				} else {
					IsPress3or4button = false
				}*/

			if !transfer.IsPsychicGameMode { // не воздействовать на гомео-параметры в игровом режиме
				ge := gomeostas.GomeostazActionEffectArr[actionID]
				if len(ge) > 0 { // пример: 2>40,4>50,5>50,6>30,7>-20
					aeArr := strings.Split(ge, ",")
					for i := 0; i < len(aeArr); i++ {
						p := strings.Split(aeArr[i], ">")
						id, _ := strconv.Atoi(p[0])
						v, _ := strconv.ParseFloat(p[1], 64)
						gomeostas.ChangeGomeostazParametr(id, v)
					}
				}
			}
		}
		ActionFromPult[0] = 0
		ActionFromPult[actionID] = pulsCount
		ActionFromPultContext[0] = 0
		ActionFromPultContext[actionID] = pulsCount

		// эффект нажатия кнопок действий на пульте
		gomeostas.SetGomeostazActionCommonEffectArr(actionID)
	}
	// дезактивировать все акции через 10 секунд
	/* вредна, т.к. запущенный таймер может погасить нормальные акции.
	Акции должны гаситься после текущего использования в perception.go -> action_sensor.DeactivationTriggers()
	time.AfterFunc(10*time.Second, func() {
		DeactivationTriggers()
	})
	*/
	// тестирование
	// gomeostas.BetterOrWorseNow()
	return
}

// дезактивировать все пусковые стимулы при изменении условий action_sensor.DeactivationTriggers() ()
func DeactivationTriggers() {
	for i := 1; i < 18; i++ {
		ActionFromPult[i] = 0
	}
	//IsPress3or4button = false
}

// дезактивировать все контексты
func DeactivationTriggersContext() {
	for i := 1; i < 18; i++ {
		ActionFromPultContext[i] = 0
	}
}

// какие акции действуют в данный момент действий с пульта - активные контексты действий с Пульта Очищаются
func CheckCurActions() []int {
	var aArr []int
	for i := 1; i < 18; i++ {
		if ActionFromPult[i] > 0 {
			aArr = append(aArr, i)
		}
	}
	if aArr == nil {
		ActionFromPult[0] = 0
	}
	return aArr
}

// какие сохраненные акции действуют в данный момент пульса - активные контексты действий с Пульта НЕ очищаются, сохраняя контекст
func CheckCurActionsContext() []int {
	var aArr []int
	for i := 1; i < 18; i++ {
		if ActionFromPultContext[i] > 0 {
			aArr = append(aArr, i)
		}
	}
	if aArr == nil {
		ActionFromPultContext[0] = 0
	}
	return aArr
}

/////////////////////////////////////////////////////////////////////

/*
была акция с пульта - получить время акции
*/
var PultActionPulsCount = 0 // время акции
func IsPultAction() {
	PultActionPulsCount = pulsCount
}

//////////////////////////////////////////////////

____________________________________________

BOT/brain/brain.go
____________________________________________
/* общие глобальные дела */
package brain

import (
	"BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	"BOT/brain/psychic"
	"BOT/brain/reflexes"
	"BOT/brain/sleep"
	TerminalActions "BOT/brain/terminete_action"
)

var IsStressing = 0 // не успевает за 1 сек отсканировать
var isWorking = 0   // флаг блока обработки нейросети

/*
	Главный цикл (пульс) опроса состояния Beast и его ответов

активируется в pulsActions() каждый пульс
*/
func Activation(lifeTime int) {

	// true - смерть Beast при повреждении >99%
	if gomeostas.IsBeastDeath { // прекращение раздачи пульса
		return
	}
	if isWorking == 1 {
		IsStressing = 1
		return
	} else {
		IsStressing = 0
	}
	// начало обработки нейросети
	isWorking = 1
	isSlipping, sleepingType := sleep.GetSleepCondition()
	// текущее состояние гомеостаза и базового контекста с каждым пульсом
	gomeostas.GomeostazPuls(EvolushnStage, lifeTime, PulsCount, isSlipping)
	action_sensor.ActionSensorPuls(EvolushnStage, lifeTime, PulsCount, isSlipping)
	sleep.SleepPuls(EvolushnStage, lifeTime, PulsCount)
	reflexes.ReflexCountPuls(EvolushnStage, lifeTime, PulsCount, isSlipping)
	TerminalActions.TermineteActionCountPuls(EvolushnStage, lifeTime, PulsCount, isSlipping)
	psychic.PsychicCountPuls(EvolushnStage, lifeTime, PulsCount, sleepingType)
	psychic.SetNotAllowAnyActions(NotAllowAnyActions)
	// конец обработки нейросети
	isWorking = 0
}

////////////////////////////////////////////////////

/////////////////////////////////////////

____________________________________________

BOT/brain/gomeostas/BAD_detector.go
____________________________________________
/* распознаватели отклонения жизненных параметров GomeostazParams[id] от нормы

Здесь решаются две независимые задачи:
1. Определение значения  Улучшения или уходшения состояния - для оценки результата действия.
2. Оценки текщего состояния Плохо - Норма - хорошо.

1-я ЗАДАЧА
Улучшение или уходшение состояния - сигналы для оценки результата действия для automatizm_result.go,
которые передаются в психику через func BetterOrWorseNow при каждой стимуляции после действий Бота.
Для этого тслеживается ОДИН приоритетный гомео-параметр: PrioritetGomeoParID (func compareWithOld())
это соотвествует тому, что в каждый момент времени есть одна самая главная проблема (высшая значимость).
Для получения конечного сигнала ухудшения или улучшения (func BetterOrWorseNow) используется:
var GomeostazActionEffectPainV=0 // величина Боли
var GomeostazActionEffectJoyV=0 // величина радости
var GomeostazActionEffectPulsCount=0
(В psichic.mood.go: var painValue=0 var joyValue=0)
Эти значение есть даже при Норме всех жизненных параметров, если Оператор использует мотивационные Кнопки.

2-я ЗАДАЧА
Последовательность при изменении состояния:
1. Сначала распознается порции измненеия состояний по каждой шкале BadNormalWellValue в func badDetecting().
2. Изменения сохраняются в виде saveDinamicParams в func chandeDinamicPars() до следующего изменения.
3. Вычисляются суммарные отклонения по всем параметрам с учетом весов значимости параметра
curCommonBadNormalWellVal в func commonBadDetecting()
4. На пульт уходит индикация по каждому параметру и общее состояние в func GetCurGomeoStatus()
5. Экспоненциальная оценка изменения общего состояния (димит -10 и +10) commonDiffValue в func commonPerceptionNow()

При вызове func BetterOrWorseNow(kind int) возвращается информация о текущем эффекте значимости результата действия.
*/

package gomeostas

import (
	"BOT/lib"
	"math"
	"strconv"
	"strings"
)

/////////////////////////////////////////

/*
	старые значения для сравнения с PrioritetGomeoParID

Для оценки совершенного действия.
*/
var oldGomeoParID = make([]int, 10)

func initOldGomeoParID() {
	for id := 0; id < 9; id++ {
		oldGomeoParID[id] = int(GomeostazParams[id])
	}
}

// из психики: зафиксировать текущее состояние на момент срабатывания автоматизма
func BetterOrWorseInit() {
	initOldGomeoParID()
	GomeostazActionEffectPainV = 0
	GomeostazActionEffectJoyV = 0
}

// //////////////////////////////////////
// сравнить PrioritetGomeoParID со старым значением
func compareWithOld() int {
	// найти старое значение
	for id := 0; id < 9; id++ {
		if PrioritetGomeoParID == id {
			diff := 0
			if id == 1 { // Энергия наоборот
				diff = int(GomeostazParams[id]) - oldGomeoParID[id]
			} else {
				diff = oldGomeoParID[id] - int(GomeostazParams[id])
			}
			//oldGomeoParID[id] = int(GomeostazParams[id]) // сразу обновить старое состояние
			// после каждой оценки в func BetterOrWorseNow чтобы не устаревали сильно параметры.
			initOldGomeoParID()
			return diff
		}
	}
	return 0
}

////////////////////////////////////////////////////

// название Базового состояния из его ID str:=gomeostas.getBaseCondFromID(id)
func GetBaseCondFromID(id int) string {
	var out = ""
	switch id {
	case 1:
		out = "Плохo"
	case 2:
		out = "Норма"
	case 3:
		out = "Хорошо"
	}
	return out
}

// момент появления Хорошо (в тиках пульса)
//var CommonWellValueStart = 0

/* детекторы по каждому из жизненных параметров
имеют те же значения, что и в CommonBadNormalWell
Алгоритм на примере энергии.
Если энергия истощилась, то BadNormalWellValue[id] будет тем отрицательнее, чем сильнее истощилась.
Если началось восполнение энергии, то BadNormalWell[id]=3 (хорошо) и BadNormalWellValue[id] уменьшается по мере насыщения.
Но если насыщение остановилось (не меняется в течение ),
то через время BadNormalWellValue[id] снова становится ==1 (плохо), если параметр еще не восстановлен: остался еще голоден.
В природе чем сильнее голод, тем сильнее Хорошо с началом его удовлетворения и это Хорошо уменьшается с насыщением.
Но если еды было мало, то тварь довольно скоро опять почувствует голод, но уже не такой большой.
*/

// Значения динамики состояния жизненных параметров, - выходная информация детектора состояния
var BadNormalWell = make([]int, 10)      // 1 - Похо, 2 - Норма, 3 - Хорошо
var BadNormalWellValue = make([]int, 10) // насколько плохо или хорошо, значение от  -100 0 до 100 В НОРМЕ=0
//////////////////////////////////////////////////

// пороги реальной нормы
var compareNorma = make([]int, 10)

// пороги условного начала выхода параметров из нормы
var compareLimites = make([]int, 10)

func initBadDetector() {
	for id, _ := range GomeostazParams {
		BadNormalWell[id] = 2 // норма
		BadNormalWellValue[id] = 0
		dinamicValueStart[id] = 0
		//	oldBadNormalWellValue[id] = 0
	}
	/* порог выхода из нормы жиз.параметров:
	 */
	path := lib.GetMainPathExeFile()
	lines, _ := lib.ReadLines(path + "/memory_reflex/GomeostazLimits.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		/* 	В редакторе /memory_reflex/GomeostazLimits.txt порог начинается со слабого отклонения от нормы.
			Степень отклонения от нормы делится на сегменты по 1/5 всего порога,
		т.е. на критическое Плохо приходится значение порога: p[1]/5
		*/
		compareNorma[id], _ = strconv.Atoi(p[1])
		// реально плохо становится при "Значительное отклонение" - середина от ухода из нормы

		compar := compareNorma[id] / 2
		compareLimites[id] = int(compar)

		if id == 3 {
			id = 3
		}
	}
	return
}

// время в секундах удержания состояния saveDinamicParams[] для возврата в Норму
var dinamicTime = 50
var dinamicValueStart = make([]int, 10) //make(map[int]int)	// время возникновения динамического состояния Хорошо или Плохо

/*
	Определение текущего состояния по каждому базовому параметру

затем в commonBadDetecting() вычисляется общее интегральное состояние
*/
var prevGomeostazParams = make([]int, 10) // предыдыщие значения ж.параметров
var dinamicParams = make([]int, 10)       // какие парамерты изменились, в какую сторону и на сколько, (отрицательные - стало хуже)
var saveDinamicParams = make([]int, 10)   //удержание значений dinamicParams до их изменений (инд. на пульте)
var wasChangeParams = false               // были значительные изменения параметров
var prevCommonBadDetectingPulsCount = 0

func badDetecting() {
	if NotAllowSetGomeostazParams {
		return
	}

	dinamicParams = make([]int, 10) // очистить
	var difSensorPar = 2            // на какую величину изменятся параметры, чтобы это заметил детект
	isChangeDiff := false           // были значимые изменения параметров

	for id := 1; id < 9; id++ {
		curG := int(GomeostazParams[id])
		// насколько изменились параметры с прошлого сканирования, т.е. отрицательные - стало хуже
		diff := curG - prevGomeostazParams[id]
		if id > 1 { // для энергии наоборот
			diff *= -1
		}
		////////////////////////////////

		// Если нет diff (запуск или простой>50сек) то нужно определитьс с состонием статически:
		if diff == 0 {
			var isBad = false
			// только для энергии
			if id == 1 && curG < compareLimites[id] { // вышел за Сильное отклонение
				isBad = true
			}
			if id > 1 && curG > 100-compareLimites[id] { // вышел за Сильное отклонение
				isBad = true
			}
			if isBad { // когда слишком плохо
				BadNormalWell[id] = 1 // плохо
				// принимает значение параметра
				if id == 1 {
					BadNormalWellValue[id] = -(100 - curG)
				} else {
					BadNormalWellValue[id] = -curG
				}
				dinamicValueStart[id] = 0
				//continue
			}

			isWell := isNormaForGomeoParam(id) // определеие чистой нормы для параметра
			if isWell {                        // когда явная норма
				BadNormalWell[id] = 2 //
				BadNormalWellValue[id] = 0
			}
		} // КОНЕЦ if diff==0{
		///////////////////////////////////

		if PulsCount > 2 { // чтобы было уже установленные значения PulsCount!!!
			//////////// улучшения или ухудшения в диапазоне слабых отклонений от нормы
			if diff != 0 {
				if diff > difSensorPar || diff < -difSensorPar {

					if id != 7 { // не учитывать автоматическое улучшение самосохранения при малой опасности (func gomeostazUpdate())
						dinamicParams[id] = diff
						isChangeDiff = true // были значимые изменения параметров
					}

					prevGomeostazParams[id] = curG
				}

				if diff > difSensorPar { // улучшение
					// если было динамическое улучшение
					dinamicValueStart[id] = PulsCount // контроль точкой прерывания
				}
				if diff < -difSensorPar { // ухудшение
					dinamicValueStart[id] = PulsCount // контроль точкой прерывания
				}

				//if diff != 0{
			} else {
				prevGomeostazParams[id] = curG
			}

			//if PulsCount > 2 {
		} else {
			prevGomeostazParams[id] = curG
		}
	} //for id := 1; id < 9; id++ {
	/////////////////////////////

	if isChangeDiff { // были значимые изменения параметров
		chandeDinamicPars()    // сохранить до следующего изменения
		wasChangeParams = true // были изменения параметров
	} else {
		wasChangeParams = false // не было изменения параметров
	}
	// если прошли интревалы динамического Хорошо и Полохо, то погасить их
	checkDinamicTimes()

	if wasAnyChangeParams() {
		// не реагировать 2 пульса чтобы не влияли последствия изменений параметров (изменяются Стресс) Защита от дребезга
		if PulsCount-prevCommonBadDetectingPulsCount > 2 {
			prevCommonBadDetectingPulsCount = PulsCount
			commonBadDetecting()
		}
	}
}

// определеие чистой нормы для параметра
func isNormaForGomeoParam(id int) bool {
	curG := int(GomeostazParams[id])
	var isWell = false // чистая норма
	// только для энергии
	if id == 1 && curG > compareNorma[id] { // вышел в чистую норму
		isWell = true
	}
	if id > 1 && curG < compareNorma[id] { // вышел в чистую норму
		isWell = true
	}
	if isWell { // когда явная норма
		BadNormalWell[id] = 2 //
		BadNormalWellValue[id] = 0
	}
	return isWell
}

//////////////////////////////////////////////

// были значимые изменения параметров, у тех, что не изменились остается BadNormalWell[id] = 2
func chandeDinamicPars() {
	// !!! НЕТ curCommonBadNormalWellVal = 0
	saveDinamicParams = make([]int, 10)
	for id := 1; id < 9; id++ {
		saveDinamicParams[id] = dinamicParams[id]
		// чтобы BadNormalWell[] сохранились
		//BadNormalWell[id] = 2
		if dinamicParams[id] > 0 {
			BadNormalWellValue[id] = dinamicParams[id]
			BadNormalWell[id] = 3
		}
		if dinamicParams[id] < 0 {
			BadNormalWellValue[id] = dinamicParams[id]
			BadNormalWell[id] = 1
		}
	}
	return
}

// гашение Хорошо и Плохо через интервал dinamicTime в норму
func checkDinamicTimes() {
	for id := 1; id < 9; id++ {
		if dinamicValueStart[id] > 0 && (dinamicValueStart[id]+dinamicTime < PulsCount) {
			BadNormalWell[id] = 2 // норма
			BadNormalWellValue[id] = 0
			dinamicValueStart[id] = 0
		}
	}
}

////////////////////////////////__________________детектор общего базового состояния_______________

/*
	детектор общего базового состояния (индикация на Пуьте document.getElementById('common_status_id'))

1 - Похо, 2 - Норма, 3 - Хорошо (==выход из состояния Плохо)
*/
var CommonBadNormalWell = 2 // 1 - Похо, 2 - Норма, 3 - Хорошо из других пакетов: gomeostas.CommonBadNormalWell

/*
	Суммарное значение гомеопраметров с учетом весов параметров.

изменяется - в func commonBadDetecting()
в curCommonBadNormalWellVal будет нарастать изменение
curCommonBadNormalWellVal НИКОГДА НЕ Обнуляется! потому как изменения достаточно точно компенсируются
и нет критерия, когда можно было бы делать новую инициализацию.
Но и не нужно пытаться инициализировать время от времени, потому что главное - влияние текущих изменений
*/
var curCommonBadNormalWellVal = 0

// предыдущее значение  curCommonBadNormalWellVal
var OldCommonBadNormalWellVal = 0 // предыдущее curCommonBadNormalWellVal

/*
	Распознавание интегрального состояния CommonBadNormalWell

пороговый (compareLevel) сумматор значений состояний Плохо
Логика работы:
 1. Если суммарное значение Плохо выше порога - базовое состояние Плохо
 2. Если суммарное значение Плохо ниже порога и:
    2.1. если предыдущее базовое состояние было Плохо - базовое состояние Хорошо
    2.2. если предыдущее базовое состояние было Норма или Хорошо - базовое состояние Норма
 3. Если базовое состояние Хорошо держится больше dinamicTime (50 сек) - базовое состояние Норма
*/
var prevCommonBadNormalWellVal = 0 // только для func commonBadDetecting()
/*
	вариант без saveDinamicParams, непосредственное сравнение гомео-параметров при изменениях

не требует инициализации (всегда корректен).
*/
func commonBadDetecting() {
	if NotAllowSetGomeostazParams {
		return
	}

	/* Из суммы CommonBadValue исключать параметры, не влияющие на жизненные показатели:
	гон, потребность в общении, в обучении, любопытство
	*/
	prevCommonBadNormalWellVal = curCommonBadNormalWellVal
	curCommonBadNormalWellVal = 0
	isCommonWell := true // true - все важные параметры в норме
	for id := 1; id < 9; id++ {
		if id == 3 || id == 4 || id == 5 || id == 6 || id == 7 {
			continue
		}

		//если все параметры в норме, то не делать состояние плохо
		isWell := isNormaForGomeoParam(id) // определеие чистой нормы для параметра
		if !isWell {                       // когда явная норма
			isCommonWell = false
		}

		// только текущее изменение всех параметров, а не сами параметры умножаем на вес значимости параметра
		if id == 1 {
			curCommonBadNormalWellVal += (100 - int(GomeostazParams[id])) * GomeostazParamsWeight[id]
		} else {
			curCommonBadNormalWellVal += int(GomeostazParams[id]) * GomeostazParamsWeight[id]
		}
	}

	//	fmt.Print(strconv.Itoa(prevCommonBadNormalWellVal) + " - " + strconv.Itoa(curCommonBadNormalWellVal) + "\r\n")

	var compareLevel = 100  // порог начала измененияя состояния
	CommonBadNormalWell = 2 // норма

	//diff := curCommonBadNormalWellVal - prevCommonBadNormalWellVal
	if prevCommonBadNormalWellVal > curCommonBadNormalWellVal+compareLevel { // Хорошо
		CommonBadNormalWell = 3
		SetGomeoAtasEffect(3) // эффект при Критическом выходе из нормы важных жизненных параметров
	}
	//если все параметры в норме, то не делать состояние плохо
	if (curCommonBadNormalWellVal > prevCommonBadNormalWellVal+compareLevel) && !isCommonWell { // Плохо
		CommonBadNormalWell = 1 // Плохо
		SetGomeoAtasEffect(1)   // эффект при Критическом выходе из нормы важных жизненных параметров
	}
	return
}

/*
	прежний вариант, с использованием saveDinamicParams, непонятно как инициализировать время от времени
т.к. curCommonBadNormalWellVal постоянно накапливает изменения.

func commonBadDetecting() {
	if NotAllowSetGomeostazParams {
		return
	}

	// Из суммы CommonBadValue исключать параметры, не влияющие на жизненные показатели:
	//гон, потребность в общении, в обучении, любопытство
	newDiff := 0
	isCommonWell := true // true - все важные параметры в норме
	for id := 1; id < 9; id++ {
		if id == 3 || id == 4 || id == 5 || id == 6 || id == 7 {
			continue
		}

		isWell := isNormaForGomeoParam(id) // определеие чистой нормы для параметра
		if !isWell {                       // когда явная норма
			isCommonWell = false
		}

		// только текущее изменение всех параметров, а не сами параметры умножаем на вес значимости параметра
		newDiff += saveDinamicParams[id] * GomeostazParamsWeight[id]
	}

	if wasChangeParams { // были изменения параметров
		prevCommonBadNormalWellVal = curCommonBadNormalWellVal
		curCommonBadNormalWellVal += newDiff // текущие изменения модифицируют прежнее значение curCommonBadNormalWellVal
		//		oldNewDiff = newDiff
	}

	fmt.Print(strconv.Itoa(prevCommonBadNormalWellVal) + " - " + strconv.Itoa(curCommonBadNormalWellVal) + "\r\n")

	var compareLevel = 100  // порог начала измененияя состояния
	CommonBadNormalWell = 2 // норма

	//diff := curCommonBadNormalWellVal - prevCommonBadNormalWellVal
	if curCommonBadNormalWellVal > prevCommonBadNormalWellVal+compareLevel { // Хорошо
		CommonBadNormalWell = 3
		SetGomeoAtasEffect(3) // эффект при Критическом выходе из нормы важных жизненных параметров
	}
	if (prevCommonBadNormalWellVal > curCommonBadNormalWellVal+compareLevel) && !isCommonWell { // Плохо
		CommonBadNormalWell = 1 // Плохо
		SetGomeoAtasEffect(1)   // эффект при Критическом выходе из нормы важных жизненных параметров
	}
	return
}
*/
///////////////////////////////////////////////////

var prevGomeoParams = make([]int, 10) // предыдущие значения ж.параметров
func wasAnyChangeParams() bool {
	new := false
	for id := 1; id < 9; id++ {
		if id == 3 || id == 4 || id == 5 || id == 6 || id == 7 {
			continue
		}
		if prevGomeoParams[id] != saveDinamicParams[id] {
			new = true
		}
		prevGomeoParams[id] = saveDinamicParams[id]
	}
	return new
}
func initAnyChangeParams() {
	for id := 0; id < 9; id++ {
		prevGomeoParams[id] = saveDinamicParams[id]
	}
}

///////////////////////////////////////////////

/*
	Снять режимы Плохо и Хорошо при нажатии на кнопку Успокоить

По времени dinamicTime они снимаются в func checkDinamicTimes()
*/
func CliarWellBad() {

	for id := 1; id < 9; id++ {
		saveDinamicParams[id] = 0
		BadNormalWellValue[id] = 0
		dinamicValueStart[id] = 0
		BadNormalWell[id] = 2 // норма
	}
	OldCommonBadNormalWellVal = curCommonBadNormalWellVal
	CommonBadNormalWell = 2 //  норма
}

///////////////////////////////////////////////////////////////

// //////////////////////////////////////////////////////////////
// для Пульта индикация слайдеров и общее Плохо-Норма-Хорошоы
func GetCurGomeoStatus() string {
	var out = "0;" + strconv.Itoa(CommonBadNormalWell) + "|"
	//for id, v := range BadNormalWell {
	for id := 1; id < 9; id++ {
		out += strconv.Itoa(id) + ";" + strconv.Itoa(BadNormalWell[id]) + "|"
	}
	out += "@0|"
	for id := 1; id < 9; id++ {
		out += strconv.Itoa(saveDinamicParams[id]) + "|"
	}
	return out
}

/////////////////////////////////////////////////

/* Стало лучше или хуже?.
Вызывается из психики res:=gomeostas.BetterOrWorseNow()

BetterOrWorseNow вызывается в случаях:
1. При стимуле в период ожидания LastRunAutomatizmPulsCount (automatism_tree.go func afterTreeActivation())
2. Процесс сновидения или мечтаний (understanding_problem_tree.go func ProblemTreeActivation())

LastRunAutomatizmPulsCount устанавливается в 2-ч случаях:
1. когда было действие бота и он ждет ответ для оценки эффекта (после func RumAutomatizm )
2. Совершение произвольного действия (после func showVolutionAction)


ВОЗВРАЩАЕТ:
commonDiffValue - насколько изменилось общее состояние, значение от -10(максимально Плохо) через 0 до 10(максимально Хорошо)
GomeoParIdSuccesArr - стали лучше следующие г.параметры []int гоменостаза

Если было очень плохо, а стало не очень плохо, то commonDiffValue станет позитивным.
*/
//	var GomeoParIdBadArr []int// спиоск ж.параметров с ухудшнием
func BetterOrWorseNow() (int, []int) { // только для func BetterOrWorseNow(kind int)
	/*!!! НУЖНО ДАТЬ ВРЕМЯ НА ТО, ЧТОБЫ ПОЛУЧИТЬ НОВОЕ после Стимула ЗНАЧЕНИЕ ГОМЕОПАРАМТРОВ
	потому как действие попадает не в тот пульс, что измерение последствий, а всегда ему предшествует.
	т.е. BetterOrWorseNow нужно откладывать чуть более пульса, на 1.5 сек.
	*/
	//	time.Sleep(1500 * time.Microsecond) нормально успевают измениться параметры гомеостаза, так что не нужно

	// какие id парамектров улучшились - для Automatizm.GomeoIdSuccesArr (какие ID гомео-параметров улучшает это действие)
	var GomeoParIdSuccesArr []int // спиоск ж.параметров с улучшением
	for id := 1; id < 9; id++ {
		if saveDinamicParams[id] > 0 {
			GomeoParIdSuccesArr = append(GomeoParIdSuccesArr, id)
		}
	}
	/////////////////

	//старые значения приоритетного жизненного параметра сравниваются с новым PrioritetGomeoParID
	diff := compareWithOld() //
	diff0 := diff
	// Значение экспоненциально стремится к пределам -10 и 10
	//diff = int(10.0 - 10.0 / math.Exp(float64(lib.Abs(diff)) * 0.17))
	eval := int(11 - 11/math.Exp(float64(lib.Abs(diff))*0.1))
	if diff < 0 {
		diff = -eval
	} else {
		diff = eval
	}

	commonDiffValue := 0 // итоговый мотивационный эффект для оценки действия

	/* Боль является преимущественным мотивационным фактором, чем Радость. Детекторов боли несопоставимо больше.
	   Боль так же имеет преимущества перед улучшением жизненных показателей.
	*/
	if GomeostazActionEffectPainV > 0 { // Значение Боли
		if diff >= 0 {
			commonDiffValue = int(diff/3) - GomeostazActionEffectPainV
		} else { // diff<0 - выбрать самое плохое
			commonDiffValue = -lib.Max(-diff, GomeostazActionEffectPainV)
		}
	}

	// Если Радость
	if GomeostazActionEffectJoyV > 0 && GomeostazActionEffectPainV == 0 {
		if diff < -5 { // значительное ухудшение ж.параметра
			commonDiffValue = diff
		} else {
			commonDiffValue = lib.Max(diff, GomeostazActionEffectJoyV)
		}
	}

	lib.WritePultConsol("<span style='color:blue;background-color:#C5FFCC'>ПРАВИЛА. Эффект commonDiffValue определен в func BetterOrWorseNow(): <b>" +
		strconv.Itoa(commonDiffValue) + "</b> (гомео diff=" +
		strconv.Itoa(diff0) + ", Боль: " +
		strconv.Itoa(GomeostazActionEffectPainV) + ", радость: " +
		strconv.Itoa(GomeostazActionEffectJoyV) + ")</span>")

	// на накапливать эти состояния
	GomeostazActionEffectPainV = 0
	GomeostazActionEffectJoyV = 0

	// НЕТ curCommonBadNormalWellVal = 0

	return commonDiffValue, GomeoParIdSuccesArr
}

//////////////////////////////////////////////////////////////////

/*
	достаточно ли изменились гомео-параметры при активации дерева не оператором

чтобы проходить func consciousnessElementary()

Значения diff нужно оптимизировать при необходимости.
*/
var prevGomeoParID = make([]int, 10)

func GetGomeoParsDiff() bool {
	toAttention := false
	for id := 1; id < 9; id++ {
		if saveDinamicParams[id] > 0 {

		}
		diff := 0
		switch id {
		case 1: //Энергия
			diff = 20
		case 2: //Стресс
			diff = 40
		case 3: //Гон
			diff = 60
		case 4: //Потребность в общении
			diff = 50
		case 5: //Потребность в обучении
			diff = 50
		case 6: //Поиск
			diff = 50
		case 7: //Самосохранение
			diff = 40
		case 8: //Повреждения
			diff = 10
		}
		if lib.Abs(int(GomeostazParams[id])-prevGomeoParID[id]) > diff {
			toAttention = true
		}
		if toAttention { // достаточно сильное изменение, чтобы привлечь внимание
			for id := 1; id < 9; id++ {
				prevGomeoParID[id] = int(GomeostazParams[id])
			}
			return true
		}
	}
	return false
}

////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/gomeostas/context_reactivetion.go
____________________________________________
/* Переактивация контекстов не по гомео-событиям, а по стимулам, т.е. рефлекторная переативация.

Со стимулами действия уже связана значимость в виде боли и радости и для таких используется func ContextActiveFromStimul

На уровне психики приобретается опыт связывания значимостей с образами восприятия,
что детектируется func getExtremImportanceObject() при активации дерева автоматизмов
и если эта значимость высока, то вызывается func needPsyReactiveFromStimul

В обоих функциях есть возможность задавать стеепнь консервативности отклика
(с какого повторения сильного воздействия начнет происходить перективация).
*/

package gomeostas

import "BOT/lib"

///////////////////////////////////////////////////////////////////

/*
	Рефлекторная переактивация контекстов в зависимости от эффекта стимула, сопровождающихся болью и радостью.

effect<0 - значение боли, effect>0 - значение радости
активируется с приходом стимула-действия
Срабаывает до активации дерева рефлексов и до корректировки контекстов действием,
т.е. происходит рефлексторная переактивация.
*/

func ContextActiveFromStimul(effect int) {

	if noReactivationFromStimuls { // отключить переактивацию стимулом
		return
	}

	if !needReactiveFromStimul(effect) { // пока не реагируем
		return
	}

	reflexReactation(effect)
}

// консервативность реагирования на действие с пульта
var oldStymulEffect = 0
var oldStymulEffectCount = 0     // сколько раз повторялся тот же эффект
var conservatismStymulEffect = 2 //степень консервативности: сколько раз игнорируем
func needReactiveFromStimul(effect int) bool {
	if (lib.Abs(oldStymulEffect) < 6) || !lib.EqualSigns(oldStymulEffect, effect) { // менять контексты то0лько при повторном экстремальном стимуле
		oldStymulEffectCount = 0
		oldStymulEffect = effect
		return false
	}
	if lib.EqualSigns(oldStymulEffect, effect) {
		oldStymulEffectCount++
	}
	if oldStymulEffectCount >= conservatismStymulEffect { // нужно реагировать
		return true
	}
	oldStymulEffect = effect

	return false
}

///////////////////////////////////////////////////

//////////////////////////////////////////////////
/* Произвольная, но еще не осознанная! переактивация контекстов, например, оцененной значимостью образа.
т.е. только на уровне опыта значимости такое возможно (extremImportanceObject)
*/
func ContextActiveFromPsy(effect int) bool {
	if noReactivationFromStimuls { // отключить переактивацию
		return false
	}
	if !needPsyReactiveFromStimul(effect) { // пока не реагируем
		return false
	}

	reflexReactation(effect)
	return true
}

// консервативность реагирования на действие с пульта
var oldPsyStymulEffect = 0
var oldPsyStymulEffectCount = 0     // сколько раз повторялся тот же эффект
var conservatismPsyStymulEffect = 4 //степень консервативности: сколько раз игнорируем
func needPsyReactiveFromStimul(effect int) bool {
	if (lib.Abs(oldPsyStymulEffect) < 6) || !lib.EqualSigns(oldPsyStymulEffect, effect) { // менять контексты то0лько при повторном экстремальном стимуле
		oldPsyStymulEffectCount = 0
		oldPsyStymulEffect = effect
		return false
	}
	if lib.EqualSigns(oldPsyStymulEffect, effect) {
		oldPsyStymulEffectCount++
	}
	if oldPsyStymulEffectCount >= conservatismPsyStymulEffect { // нужно реагировать
		return true
	}
	oldPsyStymulEffect = effect

	return false
}

// ////////////////////////////////////////////////
//
//	переативировать
//
// ////////////////////////////////////////////////
// выполнение переактивации
func reflexReactation(effect int) {
	//При срабатывании от сильной боли:
	if effect < -5 {
		isStrah := false // страх
		isZlost := false // злость
		for i := 0; i < len(BaseContextActive); i++ {
			if BaseContextActive[i] && i == 8 {
				isStrah = true
			}
			if BaseContextActive[i] && i == 10 {
				isZlost = true
			}
			// сбросить все контексты
			BaseContextActive[i] = false
		}
		if !isStrah && !isZlost {
			// установить поиск,защита,страх - легальное сочетание со страницы редактора рефлексов
			BaseContextActive[2] = true
			BaseContextActive[5] = true
			BaseContextActive[8] = true
			setNewContexts() // заполнить CurStyleImage новыми значениями по порядку весов стилей
			keepingContextPulsCount = PulsCount
			return
		}
		if isZlost { // уже злится, вогнать в ступор - легальное сочетание со страницы редактора рефлексов
			BaseContextActive[7] = true
			setNewContexts() // заполнить CurStyleImage новыми значениями по порядку весов стилей
			keepingContextPulsCount = PulsCount
			return
		} else {
			// установить поиск,агрессия,злость - легальное сочетание со страницы редактора рефлексов
			BaseContextActive[2] = true
			BaseContextActive[9] = true
			BaseContextActive[10] = true
			setNewContexts() // заполнить CurStyleImage новыми значениями по порядку весов стилей
			keepingContextPulsCount = PulsCount
			return
		}
	} //if effect<-5{
	//При срабатывании от сильной радости:
	if effect > 5 {
		isDobro := false // доброта
		isLen := false   // лень
		for i := 0; i < len(BaseContextActive); i++ {
			if BaseContextActive[i] && i == 11 {
				isDobro = true
			}
			if BaseContextActive[i] && i == 6 {
				isLen = true
			}
			// сбросить все контексты
			BaseContextActive[i] = false
		}
		if !isDobro && !isLen {
			// установить Доброта - легальное сочетание со страницы редактора рефлексов
			BaseContextActive[11] = true
			setNewContexts() // заполнить CurStyleImage новыми значениями по порядку весов стилей
			keepingContextPulsCount = PulsCount
			return
		}
		if !isLen {
			// установить Лень,Доброта - легальное сочетание со страницы редактора рефлексов
			BaseContextActive[6] = true
			BaseContextActive[11] = true
			setNewContexts() // заполнить CurStyleImage новыми значениями по порядку весов стилей
			keepingContextPulsCount = PulsCount
			return
		}
	} //if effect > 5 {
}

///////////////////////////////////////////////

____________________________________________

BOT/brain/gomeostas/contexts.go
____________________________________________
/*
	БАЗОВЫЕ КОНТЕКСТЫ (основные стили поведения) - contexts.go

1	Пищевой	- Пищевое поведение, восполнение энергии, на что тратится время и тормозятся антагонистические стили поведения.
2	Поиск	- Поисковое поведение, любопытство. Обследование объекта внимания, поиск новых возможностей.
3	Игра	- Игровое поведение - отработка опыта в облегченных ситуациях или при обучении.
4	Гон	- Половое поведение. Тормозятся антагонистические стили
5	Защита	- Оборонительные поведение для явных признаков угрозы или плохом состоянии.
6	Лень	- Апатия в благополучном или безысходном состоянии.
7	Ступор	- Оцепенелость при непреодолимой опастbase_context_activnostности или когда нет мотивации при благополучии или отсуствии любых возможностей для активного поведения.
8	Страх	- Осторожность при признаках опасной ситуации.
9	Агрессия	- Агрессивное поведение для признаков легкой добычи или защиты (иногда - при плохом состоянии).
10	Злость	- Безжалостность в случае низкой оценки .
11	Доброта	- Альтруистическое поведение.
12	Сон - Состояние сна. Освобождение стрессового состояния. Реконструкция необработанной информации.

Число одновеременно активных контекстов - не более 3-х!!! Остаются только с наибольшим весом,
а лишние будут отсеиваться в порядке убывания весов контекстов.
Это имитирует распознаватель с активацией по частично-активному профилю на входе.
*/
package gomeostas

import (
	"BOT/lib"
	"sort"
	"strconv"
	"strings"
)

func init() {
	//diff:=1
	//	diff = int(11.0 - 11.0 / math.Exp(float64(lib.Abs(diff)) * 0.17))
	//diff = int(11 - 11 / math.Exp(float64(lib.Abs(diff)) * 0.1))

	return
}

////////////////////////////////////////////////////////////

/*
	CurStyleImage удерживает активные стили с гистерезиом. Только CurStyleImage опредееляет, какие стили активны.

А BaseContextActive[] лишь промежуточный массив активных рвущихся на включение стилей.

На Пульте на странице редактора гомеостаза /pages/gomeostaz.php таблица "Активности Базовых стилей"
определяет порядок переключения сочетний базовых контекстов. Переключения происходят скачкообразно:
так, если значение параметра гомеостаза находилось около границ диапазона,
то пересечения границы флуктуацией параметра может вызвать дребезг - многократное срабатывание.
ПОэтому вводится порог переключения hysteresisLimitVal.
Это повышает стабильность удержания текущего CurStyleImage
(интерпретирует латеральное торможение конкурирующих сочетаний стилей).
Если задать hysteresisLimitVal=0, то стили будут переключаться точно по таблице "Активности Базовых стилей" (по-старому).

curGomeoPotencial вычисляется в функции getCurGomeoPotencial()
*/
var hysteresisLimitVal = 10 // значение порога (в % шкал) переключения CurStyleImage
type StyleImage struct {
	Styles    [3]int // максимум три текущих стиля
	potencial int    // значение curGomeoPotencial при обновлении CurStyleImage
}

var CurStyleImage StyleImage

// заполнить CurStyleImage новыми значениями (порядок весов стилей уже есть в BaseContextActive)
func setNewContexts() {
	n := 0
	for id, _ := range BaseContextActive {
		if BaseContextActive[id] {
			CurStyleImage.Styles[n] = id
			n++
			if n > 2 {
				break
			}
		}
	}
	CurStyleImage.potencial = curGomeoPotencial
}

var curGomeoPotencial = 0

/*
	текущее значениие curGomeoPotencial Корректруется по каждому пульсу.

Вычисляется в зависимости от состояния гомео-параметров (а не стилей поведения!).
При плавном изменении значений гомеопараметро, изменяется curGomeoPotencial
и при превышении CurStyleImage.potencial на порог  hysteresisLimitVal происходит переключение на новый дежурный стиль.
*/
func getCurGomeoPotencial() {
	// отслеживается только один приоритетный параметр гомеостаза PrioritetGomeoParID
	if PrioritetGomeoParID > 0 {
		curGomeoPotencial = 100 - int(GomeostazParams[PrioritetGomeoParID])
		if PrioritetGomeoParID == 1 {
			curGomeoPotencial = 100 - curGomeoPotencial
		}
		return
	}
	curGomeoPotencial = 0
	//если PrioritetGomeoParID - значит глухая норма, CurStyleImage будет для диапазона Норма

	/* вариант отслеживания по всем шкалам параметров нверное хуже*/

}

//////////////////////////////////////////////////////////////

// название Базового контекста из его ID str:=gomeostas.GetBaseContextCondFromID(id)
func GetBaseContextCondFromID(id int) string {
	var out = ""
	switch id {
	case 1:
		out = "Пищевой"
	case 2:
		out = "Поиск"
	case 3:
		out = "Игра"
	case 4:
		out = "Гон"
	case 5:
		out = "Защита"
	case 6:
		out = "Лень"
	case 7:
		out = "Ступор"
	case 8:
		out = "Страх"
	case 9:
		out = "Агрессия"
	case 10:
		out = "Злость"
	case 11:
		out = "Доброта"
	case 12:
		out = "Сон"
	}
	return out
}

//var IsGameMode=false

/*
	масссив активностей базовых контекстов

активный - true, неактивный - false
!!!ВНИМАНИЕ! на пульте результат изменений BaseContextActive не показывается без setNewContexts()
*/
var BaseContextActive [15]bool // index 0 НЕ ИСПОЛЬЗУЕТСЯ! т.е. начинаетсмя с BaseContextActive[1]
/* массив весов (значимостей) базовых контекстов */
var BaseContextWeight [15]int // index 0 НЕ ИСПОЛЬЗУЕТСЯ!

// антагонисты контекстов
var antagonists = make(map[int][]int)

/*
	Прошивка несовместимых сочетаний контекстов

Для каждого основного контекста - антагонисты
*/
func initContextDetector() {
	antagonists = make(map[int][]int)
	path := lib.GetMainPathExeFile()
	lines, _ := lib.ReadLines(path + "/memory_reflex/base_context_antagonists.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0]) // ID параметра гомеостаза
		a := strings.Split(p[1], ",")
		for n := 0; n < len(a); n++ {
			aID, _ := strconv.Atoi(a[n])
			antagonists[id] = append(antagonists[id], aID)
		}
	}
	lines, _ = lib.ReadLines(path + "/memory_reflex/base_context_weight.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		val, _ := strconv.Atoi(p[1])
		BaseContextWeight[id] = val
	}
	for id, _ := range BaseContextWeight {
		BaseContextActive[id] = false
	}

	/* // проверка ограничителя
	BaseContextActive[1]=true
	BaseContextActive[6]=true
	BaseContextActive[9]=true
	BaseContextActive[12]=true
	BaseContextActive[9]=true
	var activedC=make(map[int]int)
	for id, v := range BaseContextActive {
		if v{
			activedC[id]=BaseContextWeight[id]
		}
	}
	//карта только активных контекстов
	keys := make([]int, 0, len(activedC))
	for k := range activedC {
		keys = append(keys, k)
	}
	//СОРТИРОВКА ПО ЗНАЧЕНИЮ даже если значения повторяются
	sort.SliceStable(keys , func(i, j int) bool {
		return activedC[keys[i]] > activedC[keys[j]]
	})
	// ограничить только первыми тремя
	if len(keys)>3 {
		keys = keys[:3]
	}
	for id, _ := range BaseContextActive {
		BaseContextActive[id]=false
		for i := 0; i < len(keys); i++ {
			if id == keys[i]{
				BaseContextActive[id]=true
			}
		}
	}
	*/
	return
}

/*
	состояние базового контекста зависит

1) от выхода из нормы жизненных параметров
2) от безусловно прошитых признаков восприятия
Антагонисты конкурируют между собой со своими весами значимости.

!!! BaseContextActive[6]=true - Ступор регулируется как отсуствие реакций в данном контексте при опасности

!!!ВНИМАНИЕ! на пульте результат изменений не показывается т.к. нет setNewContexts()
*/
func baseContextUpdate() { // корректруется по каждому пульсу

	if waitForReactivationPeriod() {
		return
	}

	getCurGomeoPotencial()

	// сначала все контексты выключены:
	//for id, _ := range BaseContextWeight {
	for id := 1; id < 9; id++ {
		//	if id != 12 { // сон не гасить
		BaseContextActive[id] = false
		//	}
	}
	// если активен сон, все остальные неактивны
	if IsSlipping {
		BaseContextActive[12] = true
		return // больше никаких других контекстов для активных действий
	} else {
		// При бодрствовании обязательно должен быть определен базовый контекст,
		// поэтому делаем по умолчанию Лень и гасим его в нужных случаях: BaseContextActive[6]=false
		// проверка в самом конце BaseContextActive[6] = true

		/* Нельзя утанавливать только один контект, это не позволит обучать в разных условиях!
		if IsGameMode {
			BaseContextActive[3] = true
			return // больше никаких других контекстов для активных действий
		}*/
	}

	// Определяем текущее сочетания активных базовых контекстов:
	for id := 1; id < 9; id++ {
		gp := int(GomeostazParams[id])
		// в каком диапазоне находтся жизненный параметр? Выше или инже порога?
		isNorma := false                         // статическая норма
		if id == 1 && gp >= compareLimites[id] { // для энергии
			isNorma = true
		}
		if id > 1 && gp < compareLimites[id] { // для остальных
			isNorma = true
		}
		if isNorma {
			// правило для данного диапазона
			rule := GomeostazActivnostArr[id][2] // норма
			// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
			activeOrPassiveContext(rule)
			continue // при явной норме не устанавливаются другие контексты
		} else { // статические диапазоны отклонения от нормы
			diapazoN := getBadDiapazon(id)
			rule := GomeostazActivnostArr[id][diapazoN] // диапазон ухудшения
			// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
			activeOrPassiveContext(rule)
		}

		/*
			if BadNormalWell[i] == 2 { // НОРМА
				// правило для данного диапазона
				rule := GomeostazActivnostArr[i][2] // норма
				// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
				activeOrPassiveContext(rule)
			}else{// установившееся значение Плохо, .а не динамические, которые учитываются внизу
				diapazoN := getBadDiapazon(i)
				rule := GomeostazActivnostArr[i][diapazoN] // диапазон ухудшения
				// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
				activeOrPassiveContext(rule)
			}
		*/

		// ДИНАМИЧЕСКИЕ ИЗМНЕНИЯ (т.е. моменты улучшения или ухудшения) перекрывают предыдущее
		if BadNormalWell[id] == 3 { // ХОРОШО
			rule := GomeostazActivnostArr[id][1] // хорошо
			// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
			activeOrPassiveContext(rule)
		}
		// перекрывает предыдущее
		if BadNormalWell[id] == 1 { // ПЛОХО
			rule := GomeostazActivnostArr[id][0]
			// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
			activeOrPassiveContext(rule)
		}

	}

	// Конкурентность антагонистов
	keys := make([]int, 0, len(BaseContextActive))
	for id, v := range BaseContextActive {
		if v {
			keys = append(keys, id)
		}
	}
	sort.Ints(keys)
	for _, id := range keys {
		for _, ida := range antagonists[id] {
			if BaseContextActive[ida] && BaseContextWeight[ida] > BaseContextWeight[id] { // активный антагонист значимее
				BaseContextActive[id] = false // погасить текущий контекст
				break                         // больше не нужно смотреть других антагонистов
			} else {
				BaseContextActive[ida] = false // погасить антогониста
			}
		}
	}
	/* ограничение на число компонентов в образе б.контекстов: их число будет не более 3-х,
	а лишние будут отсеиваться в порядке убывания весов контекстов.
	Это неплохо имитирует распознаватель с активацией по частично-активному профилю на входе.
	*/
	var activedC = make(map[int]int)
	for id, v := range BaseContextActive {
		if v {
			activedC[id] = BaseContextWeight[id]
		}
	}

	////////////////////////////////////////
	// карта только активных контекстов
	keys = make([]int, 0, len(activedC))
	for k := range activedC {
		keys = append(keys, k)
	}
	// СОРТИРОВКА ПО ЗНАЧЕНИЮ даже если значения повторяются
	sort.SliceStable(keys, func(i, j int) bool {
		return activedC[keys[i]] > activedC[keys[j]]
	})
	// ограничить только первыми тремя
	if len(keys) > 3 {
		keys = keys[:3]
	}
	for id, _ := range BaseContextActive {
		BaseContextActive[id] = false
		for i := 0; i < len(keys); i++ {
			if id == keys[i] {
				BaseContextActive[id] = true
			}
		}
	}
}

////////////////////////
/* в каком из 5 диапазоне Плохо находится Базовый параметр, если он вне Нормы
0 - предельно плохо
      2 Норма   - getBadDiapazon не вызывается при норме
3 Слабое отклонение
4 Значительное отклонение
5 Сильное отклонение
6 Критически опасное отклонение
*/
func getBadDiapazon(pID int) int {
	gp := int(GomeostazParams[pID]) // текущее значение жизненного параметра
	limit := compareNorma[pID]      // порог начала отклонения параметров из нормы

	var bad = 0
	if pID == 1 {
		bad = limit     // остаток параметра вне критического
		gp = limit - gp // убираем критическую часть
	} else {
		//bad = limit // остаток параметра вне критического
		bad = 100 - limit // остаток параметра вне критического
		gp = gp - limit
	}
	// какой процент составляет gp
	proc := int((gp * 100) / bad)
	if proc < 20 {
		return 3
	}
	if proc < 40 {
		return 4
	}
	if proc < 60 {
		return 5
	}
	if proc < 80 {
		return 6
	}

	return 0 // предельно плохо
}

/////////////////////////////////////////////////////////////////////

// активируем или пассивируем контексты по заданному правилу в http://go/pages/gomeostaz.php
var curPainValue = 0
var curJoyValue = 0

// !!!ВНИМАНИЕ! на пульте результат изменений не показывается т.к. нет setNewContexts()
func activeOrPassiveContext(rule string) {

	if waitForReactivationPeriod() {
		return
	}

	if len(rule) == 0 {
		return
	}
	// выделяем ID контекстов
	p := strings.Split(rule, ",")
	for n := 0; n < len(p); n++ {
		p[n] = strings.TrimSpace(p[n])
		if len(p[n]) == 0 {
			return
		}
		// активируем или пассивируем контексты
		cID, _ := strconv.Atoi(p[n])
		if cID > 19 && cID < 30 {
			curPainValue = cID - 20
			curJoyValue = 0
			continue
		}
		if cID > 29 {
			curJoyValue = cID - 30
			curPainValue = 0
			continue
		}
		if cID > 0 {
			BaseContextActive[cID] = true
		} else {
			BaseContextActive[-cID] = false
		}
	}
}

///////////////////////////////////////////////////

// для определения текущего сочетания ID Безовых контекстов gomeostas.GetCurContextActiveIDarr()
func GetCurContextActiveIDarr() []int {
	var out []int
	/*
		// concurrent map iteration and map write
		for id, v := range BaseContextActive {
			if v {
				out = append(out, id)
			}
		}*/
	for i := 0; i < 3; i++ {
		out = append(out, CurStyleImage.Styles[i])
	}
	return out
}

// для Пульта
func GetCurContextActive() string {
	var out = ""
	/*
		for id, v := range BaseContextActive {
			if v {
				out += strconv.Itoa(id) + ";1|"
			} else {
				out += strconv.Itoa(id) + ";0|"
			}
		}*/
	for id, _ := range BaseContextActive {
		exists := false
		for i := 0; i < 3; i++ {
			if CurStyleImage.Styles[i] == id { // есть такой активный контекст
				out += strconv.Itoa(id) + ";1|"
				exists = true
				break
			}
		}
		if !exists {
			out += strconv.Itoa(id) + ";0|"
		}
	}
	return out
}

/*
// контекст распознавания текущей фразы с Пульта для Vernike_detector.go
func GetActiveContextInfo() map[int]int {
	var activeCW = make(map[int]int)

	keys := make([]int, 0, len(BaseContextActive))
	for id, v := range BaseContextActive {
		if v {
			keys = append(keys, id)
		}
	}
	sort.Ints(keys)
	for _, k := range keys {
		activeCW[k] = BaseContextWeight[k]
	}
	return activeCW
}*/

var IsNewConditions = false    // флаг изменения условий (пусковых стимулов)
var oldBaseCondition = 0       // предыдущее базовое состояние
var oldActiveContextstStr = "" // строка ID старого сочетания активных Базовых контекстов

/*
	переактивация контекстов в зависимости от состояния гомео-параметров

детектор изменения базового состояния и контекстов - проверка по каждому пульсу
*/

func changingConditionsDetector() {

	if waitForReactivationPeriod() {
		return
	}

	if oldBaseCondition != CommonBadNormalWell {
		oldBaseCondition = CommonBadNormalWell

		correctPainJoy() // скорректировать состояния Боль, Радость

		IsNewConditions = true
		return
	}

	var activeContextstStr = ""
	keys := make([]int, 0, len(BaseContextActive))
	for k := range BaseContextActive {
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for k, v := range keys {
		if BaseContextActive[v] {
			activeContextstStr += strconv.Itoa(k) + "_" // "_" нужно разделять цифры, иначе будет ошибаться
		}
	}
	if oldActiveContextstStr != activeContextstStr {
		// можно переключать контексты если увеличение или уменьшение curGomeoPotencial превысит порог
		if lib.Abs(CurStyleImage.potencial-curGomeoPotencial) > hysteresisLimitVal {
			// заполнить CurStyleImage новыми значениями по порядку весов стилей
			setNewContexts()

			oldActiveContextstStr = activeContextstStr
			IsNewConditions = true
			return
		} /////////////////////////////////

	}
	IsNewConditions = false
}

/* Есть ли хоть какая то активность контекстов
func IsContextActive()bool {
  for _, v :=range BaseContextActive {
   if v { return true }
 }
 return false
}


// для произвольного задания текущего сочетания ID Безовых контекстов gomeostas.SetCurContextActiveIDarr()
func SetCurContextActiveIDarr(c []int) {
	// concurrent map iteration and map write
	for i := 0; i < len(c); i++ {
		BaseContextActive[c[i]]=true
	}
}*/
////////////////////////////////////////////////////////

// скорректировать состояния Боль, Радость при изменении контекcтов
func correctPainJoy() {
	GomeostazActionEffectPainV = curPainValue // величина Боли
	GomeostazActionEffectJoyV = curJoyValue   // величина радости
}
func GetCurPainJoy() (int, int) { // запрос с психики
	return GomeostazActionEffectPainV, GomeostazActionEffectJoyV
}

//////////////////////////////////////////////////////////

// //////////////////////////////////////////////////////
var noReactivationFromStimuls = false // true чтобы не работали функции переактивации
/*
вряд ли переактивация может быть продолжительной из-за changingConditionsDetector() и ContextActiveFromStimul() которые опять изменять контексты
Но на время keepingContextTime changingConditionsDetector() блокируется
а ContextActiveFromStimul() блокируется только для незначительных
*/
var keepingContextTime = 20
var keepingContextPulsCount = 0

func waitForReactivationPeriod() bool {
	if keepingContextPulsCount == 0 {
		return false
	}
	if PulsCount-keepingContextPulsCount < keepingContextTime {
		// на время keepingContextTime сохраняются контексты после переактиваций
		return true
	}
	keepingContextPulsCount = 0
	baseContextUpdate()
	changingConditionsDetector() //переактивация контекстов в зависимости от состояния гомео-параметров
	setNewContexts()             // заполнить CurStyleImage новыми значениями по порядку весов стилей
	return false
}

///////////////////////////////////////////////////////////

____________________________________________

BOT/brain/gomeostas/gomeostas.go
____________________________________________
/*
	параметры определены в http://go/pages/gomeostaz.php

Жизненные параметры гомеостаза и Базовые стили поведения (базовые контексты рефлексов)

1 - энергия 2 - стресс 3 - гон 4-потребность в общении 5-потребность в обучении
6- Поиск, 7- Самосохранение, 8 - Повреждения
файл сохранения состояния параметров: files/GomeostazParams.txt
base_context_activnost.txt
Для параметров гомеостаза, напрямую не связанных с жизнеобеспечением (parMaxPulsCount: гон, потребность в общении,
потребность в обучении и любопытство) организована цикличность: при нарастании параметра до максимума,
он удерживается в течении 20 секунд, а потом сбрасывается.
Это позволяет создавать достаточные по времени периоды специфических контекстов реагирования.
*/
package gomeostas

// import word_sensor "BOT/brain/words_sensor"

import (
	"BOT/lib"
	"fmt"
	"os"
	"strconv"
	"strings"
)

//////////////////////////////////////////////////////////

/*
	Приоритетно отслеживаемый ID гомео-параметр.

Только один, самый значимый гомеопараметр является объектом приоритетного внимания к изменениям.
Это параметр, который достиг критического порога "Сильное отклонение".
Из всех параметров, превысивший этот порог выбирается самый весомый.

Если нет параметров с достаточно значимым отклонением от нормы, то PrioritetGomeoParID=0
и нет необходимости обращать внимание на жизненные параметры.

PrioritetGomeoParID имеет гистерезис срабатывания, чтобы избажать дребезга.
*/
var PrioritetGomeoParID = 0

func updatePrioritetGomeoPar() { // с каждым пульсом, после определения текущих значений параметров
	var Gomeovals = make([]int, 10) // %шкал * вес
	for id := 1; id < 9; id++ {
		valP := GomeostazParams[id]
		if id == 1 { // у энергии обратная шкала
			valP = 100 - valP
		}
		Gomeovals[id] = int(valP * float64(GomeostazParamsWeight[id]))
	}
	max := 0
	maxID := 0
	for id := 1; id < 9; id++ {
		if Gomeovals[id] > max {
			max = Gomeovals[id]
			maxID = id
		}
	}
	// а теперь просто выбрать первый ID
	PrioritetGomeoParID = maxID
	/*  а пусть даже в диапазоне нормы будет - для func getCurGomeoPotencial()
	if GomeostazParams[PrioritetGomeoParID]<30{// если все в норме, то ничего не отслеживать
		PrioritetGomeoParID=0
	}*/
}

/////////////////////////////////////////////////////////////////////////////

// Текущие значения параметров гомеостаза заполняется из файла files/GomeostazParams.txt
var GomeostazParams = make([]float64, 10) //make(map[int]float64)
// старые значения, обновляемые при ухудшении ??????
var OldGomeostazParams = make([]float64, 10) //make(map[int]float64)
// скорость изменения параметров со временем
var GomeostazParamsSpeed = make([]int, 10) //make(map[int]int)
// веса значимости для GomeostazSensor От 0 до 100 %
var GomeostazParamsWeight = make([]int, 10) //make(map[int]int)
// правила активирования Базовых контекстов. Здесь могут быть значения для Боли и Радости (ID>12)
var GomeostazActivnostArr = make([][]string, 10) // make(map[int][]string)

// / НАСТРОЕНИЕ ПРИ ПОСЫЛКЕ СООБЩЕНИЯ С ПУЛЬТА
var moodeDialogEffects = make([]int, 30) // используются только с 20 по 26
// нажатия кнопок действий Пульта дают гомео-эффект:
var GomeostazActionEffectArr = make([]string, 30) //make(map[int]string)
/*
	нажатия кнопок действий Пульта дают значение эффекта Боли и Радости

На уровне рефлексов не оказывает действия - только в период ожидания результата действий Beast
- для (де)мотивации в automatizm_result.go
*/
var GomeostazActionCommonEffectArr = make([]string, 30) //make(map[int]string)

/* ДЕТЕКТОРЫ ТРАВМАТИЧЕСКИХ и УЛУЧШАЮЩИХ ПОСЛЕДСТВИЙ
Величина "Боли" или "радости" - вкавычках потому что на этом уровне уровне рефлекснв нет ощещния Боли,
а есть только детекторы травматических последствий.
Существуют рецепторы, которые могут реагировать на улучшающие воздействия или на приятные стимулы.
Они известны как рецепторы приятных ощущений или рецепторы белого вещества.
Эти рецепторы обнаружены в разных частях тела и могут реагировать на приятные тактильные,
тепловые или химические стимулы.
Например, механические рецепторы на коже способны реагировать на приятное прикосновение или массаж, половые сенсоры.
Также, терморецепторы могут реагировать на приятные тепловые стимулы.
Вкусовые рецепторы в языке могут реагировать на приятные вкусы.
Эти рецепторы передают сигналы в нервную систему, вызывая положительные реакции и улучшение настроения.
Они играют важную роль в нашем восприятии и ощущении приятности, а также могут стимулировать рефлекторные реакции
и двигательные ответы.
Однако, важно отметить, что рецепторы приятных ощущений не обязательно имеют
противоположные рецепторы травматических воздействий.
Рецепторы боли и рецепторы приятных ощущений могут функционировать независимо
друг от друга и реагировать на различные типы стимулов.
Боль и радость: fornit.ru/67646

Здесь применяются детекторы общих (де)мотивирующих действий - пока только в период ожидания результата действий Beast
- для (де)мотивации в automatizm_result.go TODO но могут использовать в безусловных рефлексах.
Максимальные значения до 10.

Боль и Радость возникают при:
1. Критическом выходе из нормы важных жизненных параметров func SetGomeoAtasEffect() - проверяется с каждым пульсом
2. При мотивации Оператором нажатием кнопок func SetGomeostazActionCommonEffectArr
3. При мотивации Оператором установкой настроения при отправке сообщения func SetMoodePraseEffectArr

На рефлекторном уровне нет ощущений Боли, а есть только мотивационное действие,
оно не влияет на рефлексы вообще никак, а только - на оценку результата действий.

Гасится в func BetterOrWorseNow при каждом Стимуле после Ответа

Боль является преимущественным мотивационным фактором, чем Радость. Детекторов боли несопоставимо больше.
Боль так же имеет преимущества перед улучшением жизненных показателей.
*/
// В psichic.mood.go: var painValue=0 var joyValue=0
var GomeostazActionEffectPainV = 0 // величина Боли
var GomeostazActionEffectJoyV = 0  // величина радости
//var GomeostazActionEffectPulsCount=0  СБРАСЫВАЮТСЯ в func BetterOrWorseNow при каждом Стимуле после Ответа

// эффект нажатия кнопок действий на пульте
func SetGomeostazActionCommonEffectArr(actionID int) {
	//GomeostazActionEffectPulsCount = PulsCount
	effect := GomeostazActionCommonEffectArr[actionID]
	kind := effect[:1]
	val, _ := strconv.Atoi(effect[1:])
	if kind == "-" {
		GomeostazActionEffectPainV = val // величина Боли
	} else {
		GomeostazActionEffectJoyV = val // величина радости
	}
	return
}

// эффект настроения при отправке сообщения
func SetMoodePraseEffectArr(moodID int) {

	effect := moodeDialogEffects[moodID]
	if effect > 0 {
		GomeostazActionEffectPainV = effect // величина Боли
	} else {
		GomeostazActionEffectJoyV = effect // величина радости
	}
}

// эффект при Критическом выходе из нормы важных жизненных параметров, проверяется с каждым пульсом
func SetGomeoAtasEffect(kind int) {
	effect := 0
	if kind == 3 { // временно удерживаемое состояние Плохo (CommonBadNormalWell = 3)
		if GomeostazParams[1] < 15 { //Энергия
			effect += 5
		}
		if GomeostazParams[2] > 85 { // Стресс
			effect += 3
		}
		if GomeostazParams[8] > 60 { //Повреждения
			effect += 6
		}
		GomeostazActionEffectPainV = effect
	}
	if kind == 1 { // временно удерживаемое состояние {Хорошо} (CommonBadNormalWell = 1)
		if GomeostazParams[1] > 85 { //Энергия
			effect += 4
		}
		if GomeostazParams[2] < 15 { // Стресс
			effect += 2
		}
		if GomeostazParams[8] < 15 { //Повреждения
			effect += 5
		}
		GomeostazActionEffectJoyV = effect
	}
}

//////////////////////////////////////////////////

var PeriodPulsCount = 20             // 20 пульсов удерживается максимальное значение некритичных для жизни 4-х параметров
var parMaxPulsCount = make([]int, 4) // 0 - гон, 1 -потребность в общении, 2 -потребность в обучении, 3 - любопытство

func init() {
	/*
		// подбор нужной крутизны экспоненты для BetterOrWorseNow() - множителем k
		var str="|"
		var val=0.0
		var k=0.17
		for i := 0; i < 70; i++ {
			if i>0 && i%10==0{str+="\r\n"}
			val=10.0 - 10.0/math.Exp(float64(i)*k)
			str+=strconv.Itoa(i)+":"+fmt.Sprintf("%10.2f", val)+"|"
		}
	*/
	path := lib.GetMainPathExeFile()
	lines, _ := lib.ReadLines(path + "/memory_reflex/GomeostazParams.txt")
	if len(lines) < 7 { // испорчен файл, восстановить
		var def = "1|10\r\n2|10\r\n3|0\r\n4|0\r\n5|0\r\n6|0\r\n7|0\r\n8|0"
		lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/GomeostazParams.txt", def)
		lines, _ = lib.ReadLines(path + "/memory_reflex/GomeostazParams.txt")
	}
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		val, _ := strconv.ParseFloat(p[1], 32)
		GomeostazParams[id] = val
		OldGomeostazParams[id] = val
	}
	lines, _ = lib.ReadLines(path + "/memory_reflex/GomeostasWeight.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		weight, _ := strconv.Atoi(p[1])
		speed, _ := strconv.Atoi(p[2])
		GomeostazParamsWeight[id] = weight
		GomeostazParamsSpeed[id] = speed
	}

	GomeostazActivnostArr = make([][]string, 10) //make(map[int][]string)
	lines, _ = lib.ReadLines(path + "/memory_reflex/base_context_activnost.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0]) // ID параметра гомеостаза
		GomeostazActivnostArr[id] = make([]string, 7)
		GomeostazActivnostArr[id][0] = p[1] // плохо
		GomeostazActivnostArr[id][1] = p[2] // хорошо
		GomeostazActivnostArr[id][2] = p[3] //Норма
		GomeostazActivnostArr[id][3] = p[4] //Слабое отклонение
		GomeostazActivnostArr[id][4] = p[5] //Значительное отклонение
		GomeostazActivnostArr[id][5] = p[6] //Сильное отклонение
		GomeostazActivnostArr[id][6] = p[7] //Критически опасное отклонение
	}

	////////////////////////////////////////////////////////////
	GomeostazActionEffectArr = make([]string, 30)       //make(map[int]string)
	GomeostazActionCommonEffectArr = make([]string, 30) //make(map[int]string)
	lines, _ = lib.ReadLines(path + "/memory_reflex/Gomeostaz_pult_actions.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0]) // ID параметра гомеостаза
		GomeostazActionEffectArr[id] = p[1]
		GomeostazActionCommonEffectArr[id] = p[2] // значение эффекта Боли и Радости
	}

	lines, _ = lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/moode_dialog_effects.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		val, _ := strconv.Atoi(p[1])
		moodeDialogEffects[id] = val
	}

	initBadDetector()
	initContextDetector()
	initOldGomeoParID()

	commonBadDetecting() // для начала
	return
}

/*
	приравнять OldGomeostazParams GomeostazParams

При каждом сравнении старого с новым приравнивать Для определения вектора изменения с каждым пульсом
*/
func copyToOldPsrams() {
	//for id, _ := range GomeostazParams { ГОНКИ чтения-запись ПОЯВЛЯЮТСЯ ТОЛЬКО ПРИ range
	for id := 1; id < 9; id++ {
		OldGomeostazParams[id] = GomeostazParams[id]
	}
}

/* ПУЛЬС ГОМЕОСТАЗА - обработка раз в секунду */
var PulsCount = 0      // передача тика Пульса из brine.go
var LifeTime = 0       // время жизни в числе пульсов
var EvolushnStage = 0  // стадия развития
var IsSlipping = false // флаг фазы сна
// коррекция текущего состояния гомеостаза и базового контекста с каждым пульсом
func GomeostazPuls(evolushnStage int, lifeTime int, puls int, isSlipping bool) {
	LifeTime = lifeTime
	EvolushnStage = evolushnStage
	PulsCount = puls // передача номера тика из более низкоуровневого пакета
	IsSlipping = isSlipping

	if EvolushnStage >= 3 { // Период подражания
		IsLevelBeginParam5 = true // уровень развития для Потребность в обучении достигнут
	}
	if EvolushnStage >= 4 { //Период преступной инициативы
		IsLevelBeginParam3 = true // уровень развития для Гона достигнут
	}
	gomeostazUpdate()

	badDetecting()
	// детектор изменения базового состояния и контекстов - проверка по каждому пульсу
	changingConditionsDetector()
	// При каждом сравнении старого с новым приравнивать Для определения вектора изменения с каждым пульсом
	copyToOldPsrams()

	updatePrioritetGomeoPar() // опредеелние приоритетного жизненного параметра
}

var NotAllowSetGomeostazParams = false // флаг процесса изменения величины базового параметра
// изменить параметр на величину
func ChangeGomeostazParametr(id int, diff float64) {
	NotAllowSetGomeostazParams = true
	OldGomeostazParams[id] = GomeostazParams[id]
	GomeostazParams[id] += diff
	if GomeostazParams[id] < 0 {
		GomeostazParams[id] = 0
	}
	if GomeostazParams[id] > 100 {
		GomeostazParams[id] = 100
	}
	NotAllowSetGomeostazParams = false
}

// выдать текущие значения жизненных параметров
func GetCurGomeoParams() string {
	if NotAllowSetGomeostazParams {
		return ""
	}
	var out = ""
	//for id, _ := range GomeostazParams { ГОНКИ чтения-запись ПОЯВЛЯЮТСЯ ТОЛЬКО ПРИ range
	for id := 1; id < 9; id++ {
		out += strconv.Itoa(int(id)) + ";" + strconv.Itoa(int(GomeostazParams[id])) + "|"
	}
	return out
}

// установка параметров гомеостаза с Пульта
func SetCurGomeoParams(parID int, parVal string) {
	NotAllowSetGomeostazParams = true
	GomeostazParams[parID], _ = strconv.ParseFloat(parVal, 64)
	SaveCurrentGomeoParams()
	NotAllowSetGomeostazParams = false
	/* определить базовые контексты при новых пераметрах гомеостаза baseContextUpdate()
	потом очистить сенсоры слов и стек слов и переактивировать Дерево понимания */
	baseContextUpdate()
	//fmt.Println("SET: ", p0Arr[0], p0Arr[1])
	return
}

func ClinerAllGomeoParams(value float64) {
	GomeostazParams[1] = value
	GomeostazParams[2] = 0.0
	GomeostazParams[3] = 0.0
	GomeostazParams[4] = 0.0
	GomeostazParams[5] = 0.0
	GomeostazParams[6] = 0.0
	GomeostazParams[7] = 0.0
	GomeostazParams[8] = 0.0
}

///////////////////////////////

// до определенной стадии развития Гон и Потребности в обучении не влияют ни на что (нет у детей гона)
var IsLevelBeginParam3 = false // true - уровень развития для Гона достигнут
var IsLevelBeginParam5 = false // true - уровень развития для Потребность в обучении достигнут

// корректировка жизненных параметров по каждому пульсу
func gomeostazUpdate() {
	if NotAllowSetGomeostazParams {
		return
	}

	// изменение со временем
	changingParVal(1) // !!! на действия затрачивается много энергии http://go/pages/terminal_actions.php
	changingParVal(2)
	// Гон начинает изменяться с некоторого уровня развития Beast
	if IsLevelBeginParam3 {
		changingParVal(3)
	}
	changingParVal(4)
	// Потребность в обучении начинает изменяться с некоторого уровня развития Beast
	if IsLevelBeginParam5 {
		changingParVal(5)
	}
	changingParVal(6)
	changingParVal(7)

	// эксклюзивные зависимости
	// повреждение
	// При энергии <5% начинает увеличивается cкорость поврежедения
	var oldValGomeostazParamsSpeed8 = GomeostazParamsSpeed[8]
	if GomeostazParams[1] < 5 {
		// при GomeostazParams[1]==0 прибавка скорости будет 0, при при GomeostazParams[1]==0 прибавка скорости станет 100
		GomeostazParamsSpeed[8] += 100 - int(GomeostazParams[1]*20.0)
	}
	// при повышенном стрессе:
	if GomeostazParams[2] > 70 {
		// при GomeostazParams[2]==70 прибавка скорости будет 0, при при GomeostazParams[2]==100 прибавка скорости станет 50
		GomeostazParamsSpeed[8] += int((GomeostazParams[2] - 70.0) * 1.7)
	}
	changingParVal(8)
	GomeostazParamsSpeed[8] = oldValGomeostazParamsSpeed8
	// При 100% повреждений  - смерть. Фон пульта менятся от повреждений, начиная с критического порога.
	// Смерть - черный фор с траурной надписью, сброс памяти.

	//самосохранение  начинает ухудшаться при превышении compareLimites[1] (Значительное отклонение)
	val1 := 0.0
	var limt = float64(compareLimites[1])
	if (limt - GomeostazParams[1]) > 0 {
		val1 = 1
	}
	val2 := 0.0
	if GomeostazParams[2] > 100-limt {
		val2 = 1
	}
	val3 := 0.0
	if GomeostazParams[8] > 100-limt {
		val3 = 1
	}
	add := val1 + val2 + val3

	if add > 1 {
		add = 1
	} // ограничить порцию изменения
	if add > 0 {
		GomeostazParams[7] += add
	} else { // если нет факторов риска, то уменьшать GomeostazParams[7]

		GomeostazParams[7] -= 5
	}

	if GomeostazParams[7] < 0 {
		GomeostazParams[7] = 0
	}
	if GomeostazParams[7] > 100 {
		GomeostazParams[7] = 100
	}

	if PulsCount > 1 && PulsCount%10 == 0 { // записать в файл текущее состояние гомеостаза раз в 10 сек
		SaveCurrentGomeoParams()
	}
	baseContextUpdate()

	/* Для параметров гомеостаза, напрямую не связанных с жизнеобеспечением (parMaxPulsCount: гон, потребность в общении,
	потребность в обучении и любопытство) организована цикличность: при нарастании параметра до максимума,
	он удерживается в течении 20 секунд, а потом сбрасывается.
	Это позволяет создавать достаточные по времени периоды специфических контекстов реагирования.
	*/
	if GomeostazParams[3] > 90 && parMaxPulsCount[0] == 0 {
		parMaxPulsCount[0] = PulsCount
	}
	if GomeostazParams[4] > 90 && parMaxPulsCount[1] == 0 {
		parMaxPulsCount[1] = PulsCount
	}
	if GomeostazParams[5] > 90 && parMaxPulsCount[2] == 0 {
		parMaxPulsCount[2] = PulsCount
	}
	if GomeostazParams[6] > 90 && parMaxPulsCount[3] == 0 {
		parMaxPulsCount[3] = PulsCount
	}
	// сброс после периода удержания
	if GomeostazParams[3] > 90 {
		if parMaxPulsCount[0]+PeriodPulsCount < PulsCount {
			parMaxPulsCount[0] = 0
			GomeostazParams[3] = 0
		}
	} else {
		parMaxPulsCount[0] = 0
	}

	if GomeostazParams[4] > 90 {
		if parMaxPulsCount[1]+PeriodPulsCount < PulsCount {
			parMaxPulsCount[1] = 0
			GomeostazParams[4] = 0
		}
	} else {
		parMaxPulsCount[1] = 0
	}

	if GomeostazParams[5] > 90 {
		if parMaxPulsCount[2]+PeriodPulsCount < PulsCount {
			parMaxPulsCount[2] = 0
			GomeostazParams[5] = 0
		}
	} else {
		parMaxPulsCount[2] = 0
	}

	if GomeostazParams[6] > 90 {
		if parMaxPulsCount[3]+PeriodPulsCount < PulsCount {
			parMaxPulsCount[3] = 0
			GomeostazParams[6] = 0
		}
	} else {
		parMaxPulsCount[3] = 0
	}
}

// Сохрнаить значения параметров гомеостаза в файле
func SaveCurrentGomeoParams() {
	if len(GomeostazParams) < 7 {
		return
	}
	var fStr = "1|" + strconv.Itoa(int(GomeostazParams[1])) + "\n" +
		"2|" + strconv.Itoa(int(GomeostazParams[2])) + "\n" +
		"3|" + strconv.Itoa(int(GomeostazParams[3])) + "\n" +
		"4|" + strconv.Itoa(int(GomeostazParams[4])) + "\n" +
		"5|" + strconv.Itoa(int(GomeostazParams[5])) + "\n" +
		"6|" + strconv.Itoa(int(GomeostazParams[6])) + "\n" +
		"7|" + strconv.Itoa(int(GomeostazParams[7])) + "\n" +
		"8|" + strconv.Itoa(int(GomeostazParams[8]))

	file, err := os.Create(lib.MainPathExeFile + "/memory_reflex/GomeostazParams.txt")
	if err != nil {
		fmt.Println("Unable to create file:", err)
		os.Exit(1)
	}
	defer file.Close()
	_, _ = file.WriteString(fStr)
}

// шаг изменения парамктра со скоростью GomeostazParamsSpeed
func changingParVal(id int) {
	if NotAllowSetGomeostazParams {
		return
	}
	step := float64(GomeostazParamsSpeed[id]) / 3600
	if id == 1 {
		ChangeGomeostazParametr(id, -step)
	} else {
		ChangeGomeostazParametr(id, step)
	}
}

var IsBeastDeath = false // true - смерть Beast при повреждении >99% gomeostas.IsBeastDeath
// true - смерть Beast при повреждении > 99%
func CheckBeastDeath() bool {
	if GomeostazParams[8] > 99.0 {
		IsBeastDeath = true
		return true
	}
	return false
}

____________________________________________

BOT/brain/gomeostas/gomeostas_conditions.go
____________________________________________
/* функции использования текущих условий гомесотаза
 */

package gomeostas

import "sort"

/*
	выявить ID параметров гомеостаза как цели для улучшения в данных условиях

Возвращает PurposeGenetic.veryActual gomeostas.FindTargetGomeostazID
сортировка по уменьшению важности
*/
func FindTargetGomeostazID() (bool, []int) {
	var veryActual = false
	var idArr []int
	// BadNormalWell - состояние каждого параметра гомеостаза: 1 - Похо, 2 - Норма, 3 - Хорошо
	// отсортировать по убыли важности GomeostazParamsWeight
	badNormalWellImp := sortingForImpotents()

	for k, pID := range badNormalWellImp {
		if pID == 1 { // плохо для данного параметра гомеостаза
			idArr = append(idArr, k)
			if k == 1 || k == 2 || k == 7 || k == 8 {
				veryActual = true
			}
		}
	}
	return veryActual, idArr
}

// Сортировка ID параметров гомеостаза по убыванию значимости GomeostazParamsWeight
/* // какой-то левый вариант, было по-другому, н непонятно, почему сменил на этот. Правильный -внизу.
func sortingForImpotents() []int {
	var impC = make([]int, 110)//make(map[int]int)
	//for id, _ := range BadNormalWell {
	for id := 1; id < 9; id++ {
		impC[GomeostazParamsWeight[id]] = id
	}

	vals := make([]int, 0, len(impC))
	for k := range impC {
		vals = append(vals, k)
	}
	// СОРТИРОВКА ПО УБЫВАНИЮ
	sort.Slice(vals, func(i, j int) bool {
		return vals[i] > vals[j]
	})

	var arr = make([]int, 10)//make(map[int]int)
	for k,v := range vals {
		arr[impC[v]] = k//BadNormalWell[impC[v]]
	}

	return arr
}*/

/* // варианта Алексея
func sortingForImpotents() map[int]int{
	var arr=make(map[int]int)

	var impC = make([]int, 10)
	for id := 1; id < 9; id++ {
		impC[id] = GomeostazParamsWeight[id]
	}
	sort.Slice(impC, func(i, j int) bool {
		return impC[i] > impC[j]
	})
//	var arr = make([]int, 10)
	//for k,_ := range impC {
	for id := 1; id < 10; id++ {
		arr[id] = BadNormalWell[id]
	}

	return arr
}
*/

/*
на выходе func sortingForImpotents должна быть карта:
индекс = id параматра по убыванию значимости
значение - BadNormalWell[id]
*/
func sortingForImpotents() map[int]int {
	var impC = make(map[int]int)
	for id, _ := range BadNormalWell {
		impC[GomeostazParamsWeight[id]] = id
	}

	vals := make([]int, 0, len(impC))
	for k := range impC {
		vals = append(vals, k)
	}
	//СОРТИРОВКА ПО убыванию
	sort.Slice(vals, func(i, j int) bool {
		return vals[i] > vals[j]
	})

	var arr = make(map[int]int)

	for _, v := range vals {
		arr[impC[v]] = BadNormalWell[impC[v]]
	}

	return arr
}

/////////////////////////////////////////////////////////

// потребность общаться: гон, общение, обучение, поиск
func IsNeedForCommunication() bool {
	if GomeostazParams[3] > 85 || GomeostazParams[4] > 50 || GomeostazParams[5] > 50 || GomeostazParams[6] > 50 {
		return true
	}
	return false
}

///////////////////////////////////////////////

____________________________________________

BOT/brain/init.go
____________________________________________
/* инициализация при запуске */

package brain

import (
	"BOT/lib"
	"strconv"
	"strings"
)


// самая первая инициализация (из main.go) после всех приготовлений, до запуска пульса в puls.go
func RunInitialisation() {


//	lib.WritePultConsol("замечание <span style='ssss'>форматирование</span> <b>Bold</b>")

	str,_ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/life_time.txt")
	if len(str) > 0 {
		LifeTime, _ = strconv.Atoi(strings.TrimSpace(str[0]))
	} else {
		lib.TodoPanic("ОБНУЛИЛОСЬ ВРЕМЯ ЖИЗНИ LifeTime!")
	}
	str,_ = lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/stages.txt")
	EvolushnStage,_ = strconv.Atoi(strings.TrimSpace(str[0]))
}
///////////////////////////////////////



____________________________________________

BOT/brain/psychic/0_main_psychic.go
____________________________________________
/*
	Общий файл психики

Психика активируется PsychicInit() после активации всех предшествующих структур.

По каждому событию с Пульта или изменению состояния организма активируется дерево автоматизмов.
Если есть автоматизм - перед его выполнением вызывается ориентировочный рефлекс привлечеия внимания orientation_2()
если нет автоматизма - ориентировчный рефлекс оценки ситуации orientation_1()
Привлечение осознанного внимания выявляет конечную цель - найти автоматизм или ничего не делать.

На уровне наивности (нет уверенно решающих проблему автоматизмов)
на первый план выходит отзеркаливание чужих действий и случайные пробы-ошибки.

Понимание смысла воспринимаемого и своих действий начинается с предопределенных
генетические целей действий Beast ID гомео-параметров, которые призвано улучшить данное действие - по его ID:
TerminalActionsTargetsFromID - это – наследственно заданная цель действия, не осознаваемая при его совершении.
Но с опытом каждому действию в конкретных условиях (и к ним добавляются слова и фразы)
будет ассоциироваться смысл (осознаваемая значимость).
Таким образом, отзеркаливая чужие "зеркальные" действия и совершая свои с оценкой результата,
будет пополняться МОДЕЛЬ ПОНИМАНИЯ при данных условиях.
Эта модель, фактически, будет составлена из наборов автоматизмов, привязанных к активной ветке дерева автоматизмов,
из которых один - текущий актуальный, остальные отбракованные и предположительные.
Автоматизмы с внутренними, ментальными действиями будут обеспечивать произвольность.

Это – наследственно заданная цель действия, не осознаваемая при его совершении.
Но с опытом каждому действию в конкретных условиях (и к ним добавляются слова и фразы)
будет ассоциироваться смысл (осознаваемая значимость).

Безусловные рефлексы психики прописываются в виде функций обработки
текущей инфрмационной среды CurrentInformationEnvironment.
В этой среде активируются текущие проблемы и доминанты нерешенной проблемы.

НЕ ЗАБЫВАТЬ для всех функций произвольной активации (по актуализации текущего самоощущения)
ставить блокировку по brain.NotAllowAnyActions - if brain.NotAllowAnyActions{ return }
*/
package psychic

import (
	"BOT/lib"
	"strconv"
)

///////////////////////////////

// true - ПРИ ТЕСТИРОВАНИИ СОХРАНЯТЬ В ФАЙЛАХ ВСЕ ЭЛЕМЕНТЫ
var doWritingFile = false // true  false

// переключатель игрового режима  psychic.IsGameMode
//var IsGameMode=false
// переключатель произвольной активации игрового режима
//var IsArbitraryGameMode = false

///////////////////////////////////////////////////////////////////////

// инициализирующий блок - в порядке последовательности инициализаций
// после condition_reflex.go
func PsychicInit() {

	if EvolushnStage < 2 { // еще нет психики
		return
	}
	automatizmTreeInit()
	loadActionsImageArr()
	automatizmInit()
	loadAmtzmNextString()
	emotionsInit()
	loadActivityInit()
	verbalInit()
	cerebellumReflexInit()

	loadEpisodicTree()
	loadEpisodicMentalTree()

	initCurrentInformationEnvironment()

	loadMentalCyckleEffectArr()

	loadSituationImage()
	loadThemeImageFromIdArr()
	//уже есть  loadPurposeImageFromIdArr()
	// уже есть loadProblemTree()
	UnderstandingTreeInit()
	ProblemTreeInit()

	loadProblemDominenta()
	// saveActionImageArr()// сохранить образы сочетаний ответных действий

	// просыпание - создание базового самоощущения CurrentInformationEnvironment
	wakingUp()

	//	SensorActivation(1,1,[]int{1})
	/*
		atmzm:=findAnySympleRandActions()
		if atmzm!=nil{	}
	*/

	//	FormingMirrorAutomatizmFromList("/mirror_reflexes_basic_phrases/1_2.txt")

	//	FormingMirrorAutomatizmFromTempList("/lib/mirror_basic_phrases_common.txt")

	/*	lib.NoReflexWithAutomatizm = true
		lib.ActionsForPultStr = "0|qqqqqq||1|rrrrrrrrrrrrrr||3|wwwwwwwwwwww"
		lib.ActionsForPultStr = lib.SharedReflexWithAutomatizm()
	*/
	return
}

/////////////////////////////////////////////////////////////

// ПУЛЬС психики
var PulsCount = 0 // передача тика Пульса из brine.go
var LifeTime = 0
var EvolushnStage = 0       // стадия развития
var IsSleeping = false      // сон без сновидений
var IsSleepingDream = false // фаза сновидений сна
var WakeUppingActivation = true

func PsychicCountPuls(evolushnStage int, lifeTime int, puls int, sleepingType int) {

	if evolushnStage < 2 { // недостаточная стадия развития
		return
	}

	LifeTime = lifeTime
	EvolushnStage = evolushnStage
	PulsCount = puls // передача номера тика из более низкоуровневого пакета
	if sleepingType > 0 {
		IsSleeping = true
		if sleepingType == 2 {
			IsSleepingDream = true
		}
	}

	// тики в automatizm_result.go для удобства
	orientarionPuls()
	automatizmActionsPuls()
	moodePulse()

	if lastAutomatizmRunPulsCount > 0 && (lastAutomatizmRunPulsCount+100 < PulsCount) {
		CurrentInformationEnvironment.IsIdleness100pulse = true
	}

	// в условии NoautomatizmAfterStimul>2 чтобы не повторялось lib.WritePultConsol
	if NoautomatizmAfterStimul > 2 && (NoautomatizmAfterStimul < PulsCount-2) && PulsCount > 5 {
		// уже 2 пульса как нет автоматизма в ответ на Стимул, значит нет периода ожидания
		NoautomatizmAfterStimul = 2 // - сигнал детектора отсуствия автоматизма на Стимул оператора
		lib.WritePultConsol("<span style='color:blue;background-color:#C5FFCC'>ПРАВИЛА. Уже 2 пульса как нет автоматизма в ответ на Стимул, значит нет периода ожидания (установлено: NoautomatizmAfterStimul=2)</span>")
	}

	if IsSleeping {
		sleepingProcess()
	}

	if IsSleeping {
		IsFirstActivation = true

	} else {
		if IsFirstActivation {
			ReadiStatus = 1 //готовность Beast Для пульта:
		}

		if evolushnStage > 3 && PulsCount == 4 {

		}

		// осознание при включении и бодрствовании - один раз
		if evolushnStage > 3 && PulsCount > 4 && IsFirstActivation {
			// начать мышление
			WakeUpping() // в understanding.go
			// первый запуск при пробуждении
			ReadiStatus = 2 ////готовность Beast Для пульта

			// запустить дерево автоматизмов в первый раз, иначе не будет информации для дерева проблем.
			automatizmTreeActivation()
			//!!	MentalReasonBlocing = consciousnessElementary() - будет запущен после automatizmTreeActivation()
			beginMentalCycle() // начать первый, главный цикл мышлления с вызовом func infoFunc8()
		}

		if evolushnStage > 3 && PulsCount > 5 {
			dispetchConsciousnessThinking() // постоянная циркуляция циклов мышления

			if isNeedForCommunication() { // нужно провоцировать оператора
				// провокация в infoFunc31(c)
				mentalInfoStruct.noOperatorStimul = true
			}
		}
	}

	return

	// просыпание - создание базового самоощущения CurrentInformationEnvironment
	//	if psychicPulsCount>3 {
	//		wakingUp()
	//	}

	//		CurrentInformationEnvironment.ActionsImageID=ActivityFromIdArr[1].ID// образ бездействия
	//		CurrentInformationEnvironment.LifeTime=LifeTime

}

/*
	готовность Beast Для пульта:

0 - Beast еще не пришел в себя, общение невозможно.
1 - Психика Beast активрована, но без осознания.
2 - Beast готов к общению.
*/
var ReadiStatus = 0

func GetPsichicReady() string {
	return strconv.Itoa(ReadiStatus)
}

// ////////////////////////////////////////////////////////////
var NotAllowAnyActions = false

func SetNotAllowAnyActions(notAllow bool) {
	NotAllowAnyActions = notAllow
}

///////////////////////////////////////

// просыпание - создание базового самоощущения CurrentInformationEnvironment
func wakingUp() {

	// осознание самоощущения
	SensorActivation(1)

	// очистить всякое при просырании
	usedActIdArr = nil
	UsedPraseIdArr = nil
}

/////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
/*  активация по событиям с Пульта - из perception.go
Для блокировки активации дерева рефлексов вернуть true
*/
var firstStadiesWarning = true // защелка от повторов
// var pevPulsCount=0// не активировать в том же пульсе (возникает паразитная повторная активация)
var ActivationTypeSensor = 0 //передача типа акетивации в психику из рефлексов
func SensorActivation(activationType int) bool {
	if PulsCount < 4 { // || pevPulsCount == PulsCount
		return false
	}
	//pevPulsCount=PulsCount
	ActivationTypeSensor = activationType

	if EvolushnStage < 2 { // недостаточная стадия развития
		if firstStadiesWarning {
			firstStadiesWarning = false
			lib.WritePultConsol("Стадия развития " + strconv.Itoa(EvolushnStage) + " НЕДОСТАТОЧНА ДЛЯ АВТОМАТИЗМОВ")
		}
		return false
	}

	//  получение текущего состояния информационной среды: отражение Базового состояния и Активных Базовых контекстов
	//!!!! GetCurrentInformationEnvironment() только при ориентировочном рефлексе - смена самоощущения !!!

	atomatizmID := automatizmTreeActivation()
	if atomatizmID > 0 {

		return true
	}

	return false
}

/////////////////////////////////////////////////////////////////

////////////////////////////////////
/* Блокировать выполнение рефлексов на время ожидания результата автоматизма
вызывается из reflex_action.go рефлексов
*/
func NotAllowReflexesAction() bool {
	if MotorTerminalBlocking {
		return true
	}
	return false
}

////////////////////////////////////

// /////////////////////////////////
func SaveAllPsihicMemory() {
	notAllowScanInTreeThisTime = true
	SaveEmotionArr()
	SaveActivityFromIdArr()
	SaveVerbalFromIdArr()
	SaveAutomatizmTree()
	SaveAutomatizm()
	SaveAmtzmNextString()
	SaveSituationImage()
	SaveActionsImageArr()
	SaveCerebellumReflex()
	SaveUnderstandingTree()
	SaveProblemTree()

	SaveEpisodicTree()
	SaveEpisodicMentalTree()

	SaveMentalCyckleEffectArr()
	SavePurposeImageFromIdArr()

	SaveThemeImageFromIdArr()
	saveInterruptMemory()

	SaveProblemDominenta()

	notAllowScanInTreeThisTime = false
}

//////////////////////////////////////

// /////////////////////////////////////
func GetExtandIndoForPult() string {
	if detectedActiveLastNodID == 0 {
		return ""
	}

	return "Инфо: <span title='ID базового состояния 1 - Похо, 2 - Норма, 3 - Хорошо'>BaseID=<b>" + strconv.Itoa(CurrentCommonBadNormalWell) + "</b></span>," +
		"<span title='ID эмоции'>EmotionID=<b>" + strconv.Itoa(CurrentEmotionReception.ID) + "</b></span>, " +
		"<span title='ID дерева автоматизмов'>atmzmID=<b>" + strconv.Itoa(detectedActiveLastNodID) + "</b></span>, " +
		"<span title='ID дерева ситуации'>situatID=<b>" + strconv.Itoa(detectedActiveLastUnderstandingNodID) + "</b></span>, " +
		"<span title='ID дерева проблем'>problemID=<b>" + strconv.Itoa(detectedActiveLastProblemNodID) + "</b></span> " +
		// доп.инфа:
		//"<span title='XXXXX'>problemID=<b>"+XXXX+"</b></span> " +
		"<span style='color:#999999'>(Чтобы добавить любую нужную инфу вставь ее в 0_main_psychic.go func GetExtandIndoForPult )</span>"

} /////////////////////////////////////

____________________________________________

BOT/brain/psychic/actions_image.go
____________________________________________
/* образы действий, оператора или действий Beast.
Образ действий оператора - это Стимул (образ восприятия)
Образ действий Beast - это Акция (образ действия)

При каждой стимуляции с Пульта Дерева автоматизмов возникает образ восприятия curActiveActionsID, curActiveActions
Еще есть функция "вытащить образ Стимула ActiveActions из ID узла дерева автоматизмов"
func getActiveActionsFromAutomatizmTreeNode(automatizmTreeNodeID int)(int,*ActionsImage)

Фактически структура повторяет TriggerStimuls из рефлексов и позволяет сохранять
как образы действий в автоматизмах, так и образы действий оператора, отражаемые в дереве мот.автомтаизмов.
Используется для формирования пар стимул (действия оператора) - действия (ответ beast)
для эпизодической памяти и структуры rules - Правил примитивного опыта.

Обоснование:
Обощенные образы восприятия, возникающие в теменной ассоциативной коре полностью соотвествуют воспринимаемому
и не могут меняться.
Но в лобной коре есть отражение этих образов,
с возможностью произвольно создавать любые новые из известных элементов старого.
Поэтому для области рефлексов используется TriggerStimuls,
а для области психики - ActionsImage (с меткой Answer int // 0 - объектиное действие, 1 - субъективное предположение).
Эти два вида структур локализуются по обе стороны двигательных программ
(https://scorcher.ru/optional_class/MVAP_lections.pdf)
образуя основу "зеркальной" системы подражания
(https://scorcher.ru/axiomatics/axiom_show.php?id=522).

Формат: ID|RSarr через ,|PhraseID через ,|ToneID|MoodID|Answer
*/

package psychic

import (
	termineteAction "BOT/brain/terminete_action"
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
	"strings"
)

///////////////////////////////////////////

/*
  - это метка AmtzmNextString ID (десять миллионов) -

ID акции является цепочкой действий AmtzmNextString, а не одним действием
используется для automatizm_next.go
*/
var prefixActionIdValue = 10000000 // если ID лействия больше prefixActionIdValue, то AmtzmNextString.ID=ID-prefixActionIdValue
//////////////////////////////////////////

// универсальный образ восритятия Стимула и образ действия имеют одну и ту же структуру.
type ActionsImage struct {
	ID int // идентификатор данного сочетания пусковых стимулов
	/* метка о том, что действия не является объективным Стимулом (реально воспринятым из Пульта)
	   или реально выполненным действием (предположение, Правило из сновидения)
	*/
	Kind int // 0 - объектиное действие, 1 - субъективное предположение

	ActID []int // массив действий с Пульта или Ответного действия
	// для текущего сообщения с Пусльта:
	PhraseID []int // массив фразID (DetectedUnicumPhraseID) слова каждой фразы вытаскиваются wordSensor.GetWordArrFromPhraseID(PhraseID[0])
	ToneID   int   // тон сообщения с Пульта или Ответного действия
	/* настроение при передаче фразы с Пульта:
		20-Хорошее    21-Плохое    22-Игровое    23-Учитель    24-Агрессивное   25-Защитное    26-Протест
	ID возникает при добавлении 19 к номеру радиокнопки пульта, например, для Хорошее 1+19=20
	*/
	MoodID int // настроение оператора или Ответного действия
}

// var ActionsImageArr=make(map[int]*ActionsImage)
var ActionsImageArr []*ActionsImage // сам массив
// var AFromID = make([]*aNode, 20000)//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteActionsImageArr(index int, value *ActionsImage) {
	if index >= len(ActionsImageArr) {
		newSlice := make([]*ActionsImage, index+1)
		copy(newSlice, ActionsImageArr)
		ActionsImageArr = newSlice
	}
	ActionsImageArr[index] = value
}

// считывание члена
func ReadeActionsImageArr(index int) (*ActionsImage, bool) {
	if index >= len(ActionsImageArr) || ActionsImageArr[index] == nil {
		return nil, false
	}
	return ActionsImageArr[index], true
}

//////////////////////////////////////////1

// вызывается из psychic.go
func ActionsImageInit() {
	loadActionsImageArr()
}

// //////////////////////////////////////////////
// создать новое сочетание ответных действий если такого еще нет
var lastActionsImageID = 0

func CreateNewlastActionsImageID(id int, Kind int, ActID []int, PhraseID []int, ToneID int, MoodID int, CheckUnicum bool) (int, *ActionsImage) {
	// не создавать образ с пустым действием и вербальным сенсором - такое может быть при новом слове и отключенной галке Форсажа
	if ActID == nil && (PhraseID == nil || isUnrecognizedPhraseFromAtmtzmTreeActivation) {
		return 0, nil
	}

	if CheckUnicum {
		oldID, oldVal := checkUnicumActionsImage(Kind, ActID, PhraseID, ToneID, MoodID)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastActionsImageID++
		id = lastActionsImageID
	} else {
		if lastActionsImageID < id {
			lastActionsImageID = id
		}
	}

	var node ActionsImage
	node.ID = id
	node.Kind = Kind
	node.ActID = ActID
	node.PhraseID = PhraseID
	node.ToneID = ToneID
	node.MoodID = MoodID

	//ActionsImageArr[id]=&node
	WriteActionsImageArr(id, &node)

	if doWritingFile {
		SaveActionsImageArr()
	}

	return id, &node
}
func checkUnicumActionsImage(Kind int, ActID []int, PhraseID []int, ToneID int, MoodID int) (int, *ActionsImage) {

	for id, v := range ActionsImageArr {
		if v == nil || Kind != v.Kind {
			continue
		}
		if !lib.EqualArrs(ActID, v.ActID) {
			continue
		}
		if !lib.EqualArrs(PhraseID, v.PhraseID) {
			continue
		}
		if ToneID != v.ToneID || MoodID != v.MoodID {
			continue
		}

		return id, v
	}

	return 0, nil
}

/////////////////////////////////////////

// ////////////////// сохранить образы сочетаний ответных действий
// В случае отсуствия ответных действий создается ID такого отсутсвия, пример такой записи: 2|||0|0|
// ID|ActID через ,|PhraseID через ,|ToneID|MoodID|Answer
func SaveActionsImageArr() {
	var out = ""
	for k, v := range ActionsImageArr {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		for i := 0; i < len(v.ActID); i++ {
			out += strconv.Itoa(v.ActID[i]) + ","
		}
		out += "|"
		for i := 0; i < len(v.PhraseID); i++ {
			out += strconv.Itoa(v.PhraseID[i]) + ","
		}
		out += "|"
		out += strconv.Itoa(v.ToneID) + "|"
		out += strconv.Itoa(v.MoodID) + "|"
		out += strconv.Itoa(v.Kind)
		out += "\r\n"
	}

	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/action_images.txt", out)

}

// //////////////////  загрузить образы сочетаний ответных действий
// ID|ActID через ,|PhraseID через ,|ToneID|MoodID|Answer
func loadActionsImageArr() {
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/action_images.txt")
	cunt := len(strArr)
	ActionsImageArr = make([]*ActionsImage, cunt) //задать сразу имеющиеся в файле число
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])

		s := strings.Split(p[1], ",")
		var ActID []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			si, _ := strconv.Atoi(s[i])
			ActID = append(ActID, si)
		}

		s = strings.Split(p[2], ",")
		var PhraseID []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			si, _ := strconv.Atoi(s[i])
			PhraseID = append(PhraseID, si)
		}
		ToneID, _ := strconv.Atoi(p[3])
		MoodID, _ := strconv.Atoi(p[4])
		Kind := 0
		if len(p[5]) > 0 {
			Kind, _ = strconv.Atoi(p[5])
		}

		var saveDoWritingFile = doWritingFile
		doWritingFile = false
		CreateNewlastActionsImageID(id, Kind, ActID, PhraseID, ToneID, MoodID, false)
		doWritingFile = saveDoWritingFile
	}
	return

}

///////////////////////////////////////////

// выдать строку из одиночной или цепочечной (AmtzmNextString) акции
func GetActionsString(act int) string {
	var out = ""
	if act < prefixActionIdValue {
		return getSingleAtcStr(act)
	} else { // это последовательность действий типа AmtzmNextString
		lib.MapFree(MapGwardAutomatizmNextStringFromID)
		anArr, ok := AutomatizmNextStringFromID[act-prefixActionIdValue]
		if !ok {
			return ""
		}
		out = "<b>Цепочка действий:</b> "
		for i := 0; i < len(anArr.next); i++ {
			if i > 0 {
				out += " | "
			}
			out += getSingleAtcStr(anArr.next[i])
		}
		return out
	}

	return ""
}

// строку из одиночной акции
func getSingleAtcStr(act int) string {
	if ActionsImageArr == nil || len(ActionsImageArr) == 0 {
		return ""
	}

	//ai, ok := ActionsImageArr[act]
	ai, ok := ReadeActionsImageArr(act)
	if !ok {
		return ""
	}
	out := ""
	var kind = ""
	if ai.Kind == 0 {
		kind = "Объективнй образ"
	}
	if ai.Kind == 1 {
		kind = "Субъективнй образ"
	}
	if ai.ActID != nil {
		out += "Действие (" + kind + "): "
		for i := 0; i < len(ai.ActID); i++ {
			if i > 0 {
				out += ", "
			}
			actName := termineteAction.TerminalActonsNameFromID[ai.ActID[i]]
			out += "<b>" + actName + "</b>"
		}
		out += " "
	}

	if ai.PhraseID != nil {
		out += "Фраза: "
		for i := 0; i < len(ai.PhraseID); i++ {
			if i > 0 {
				out += " "
			}
			prase := word_sensor.GetPhraseStringsFromPhraseID(ai.PhraseID[i])
			out += "<b>\"" + prase + "\"</b>"
		}
		out += " "
	}

	if ai.ToneID != 0 {
		out += " Тон: " + getToneStrFromID(ai.ToneID) + " "
	}

	// так просто нельзя менять ActionsImageArr !!! Могту появляться дубли после изменений. Нужно обязательтно проверять на уникальность
	/*	if ai.MoodID < 0 {
				ai.MoodID *= -1
			}
			if ai.MoodID >= 19 { последний ID равен 26 а не 19!!!! т.е. if ai.MoodID >= 27 {
				ai.MoodID -= 19
			}
		Пусть остается закомментированным, если будут вылезать лажи нужно будет смотреть где и почему
			сбивается диапазон значений настроения. Просто маскировать это неправильно.
	*/
	if ai.MoodID != 0 {
		out += " Настрой: " + getMoodStrFromID(ai.MoodID) + "<br>"
	}
	return out
}

////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatism_tree.go
____________________________________________
/*  Дерево автоматизмов

Все начинается с psychic.go (atomatizmID:=automatizmTreeActivation()) -> func automatizmTreeActivation()

Это дерево активируется при:
1. Всегда при любых событиях с Пульта – так же как дерево рефлексов, но если к ветке привязан автоматизм,
то он выполняется преимущественно, блокируя рефлексы потому,
что уже было произвольностью преодолено действие рефлекса при выработке автоматизма.
Такой автоматизм обладает меткой успешности ==1. Успешность ==0 означает предположительный вариант действий,
а успешность < 0 – заблокированный вариант действия.
Так что к ветке может быть прикреплено множество неудачных и предположительных автоматизмов
и только один удачный. Более удачный результат переводит ранее удачный автоматизм в предполагаемые.
2. При произвольной активации отдельных условий.
Отсуствие подходящей для данных условий ветки дерева вызывает
Ориентировочный рефлекс привлечения внимания к активной ветке с осмыслением ситуации
и рассмотрением альтернатив действиям (4 уровня глубины рассмотрения).
При формировании нового предположительного действия создается новая ветка дерева и к ней прикрепляется автоматизм.
Т.е. новые условия не создают новой ветки, а тольно - новый автоматизм,
а пока нет автоматизма будет ориентировочный рефлекс.

У дерева фиксированных 6 уровней:
0 нулевой - основание
1 Базовое состояние - 3 вида
2 Эмоция
3 Активность с Пульта - образ ActivityFromIdArr=make(map[int]*Activity)
4 Образ контекста сообщения: сочетание Tone и Mood из структуры vrbal
5 Первый символ фразы
6 Фраза - PhraseID
До 6-го уровня - полный аналог условным рефлексам, только вместо сочетаний контекстов - эмоция.

Для оптимизации поиска по дереву перед узлом Verbal идет узел первого символа : var symbolsArr из word_tree.go

Формат записи:
ID|ParentNode|BaseID|EmotionID|ActivityID|ToneMoodID|SimbolID|PhraseID


Самоадаптация уровня Дерева автоматизмов
В результате действия автоматизма могут измениться условия и, значит,
будут запущены дерево рефлексов и опять - Дерево автоматизмов.
Возникает новая итерация адаптивности, возможно, с новым ориентировочным рефлексом второго типа.
Такой процесс может продолжаться до прихода к устойчивому состоянию.

*/

package psychic

import (
	"BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
)

// психика инициализирована
var StartPsichicNow = false

// инициализирующий блок - в порядке последовательности инициализаций
// из psychic.go
func automatizmTreeInit() {

	loadAutomatizmTree()
	if len(AutomatizmTree.Children) == 0 { // еще нет никаких веток
		// создать первые три ветки базовых состояний
		createBasicAutomatizmTree()
	}
	StartPsichicNow = true
}

/////////////////////////////////////////////////////////////

////////////////////////////////////////////

// //// ДЕРЕВО автоматизмов имеет фиксированных 6 уровней (кроме базового нулевого)
type AutomatizmNode struct { // узел дерева автоматизмов
	ID     int
	BaseID int // 1 - Похо, 2 - Норма, 3 - Хорошо, базовое состояние, !это еще не произвольно меняющееся PsyBaseMood
	/* эмоция (type Emotion struct) Эмоция может произвольно меняться, независимо от базовых контекстов
	   т.е., к примеру, при BaseID Плохо может быть позитивное EmotionID
	*/
	EmotionID  int
	ActivityID int // образ сочетания действия с Пульта
	/* образ контекста сообщения: сочетание Tone и Mood из структуры vrbal из automatism_tree_verbal_img.go
	   т.е. просто toneID+moodID - в виде строки, например: "922" = "Обычный, Хорошее"
	   дешифруется func getToneMoodStrFromID(id string)(string)
	*/
	ToneMoodID int
	SimbolID   int
	PhraseID   int // Verbal.ID // массив фраз. Может быть длинное сообщение из нескольких фраз.

	Children   []AutomatizmNode // дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID   int              // ID родителя
	ParentNode *AutomatizmNode  // адрес родителя
}

var AutomatizmTree AutomatizmNode

//var AutomatizmTreeFromID=make(map[int]*AutomatizmNode)
//var MapGwardAutomatizmTreeFromID=lib.RegNewMapGuard()
///////////////////////////////////////

var AutomatizmTreeFromID []*AutomatizmNode // узел по его ID
// var AutomatizmTreeFromID = make([]*AutomatizmNode, 20000)//задать сразу имеющиеся в файле число
// запись члена
func WriteAutomatizmTreeFromID(index int, value *AutomatizmNode) {
	if index >= len(AutomatizmTreeFromID) {
		newSlice := make([]*AutomatizmNode, index+1)
		copy(newSlice, AutomatizmTreeFromID)
		AutomatizmTreeFromID = newSlice
	}
	AutomatizmTreeFromID[index] = value
}

// считывание члена
func ReadeAutomatizmTreeFromID(index int) (*AutomatizmNode, bool) {
	if index >= len(AutomatizmTreeFromID) || AutomatizmTreeFromID[index] == nil {
		return nil, false
	}
	return AutomatizmTreeFromID[index], true
}

// последовательность узлов активной ветки
var ActiveBranchNodeArr []int

////////////////////////////////////////////////

// временная структура действий оператора (всегда с ID=0) для формирования постоянного образа curActiveActions
var curActions ActionsImage //
// структура действий оператора при активации дерева автоматизмов
var curActiveActions *ActionsImage // зеркалит текущий ActionsImage
var curActiveActionsID = 0         // ID запускаемого образа действия

// Уже есть curActions.PhraseID var curActivePhraseID=0// текущая фраза с пульта, если она есть
var curActiveTreePulsCount = 0    // время активации дерева автоматизмов в пульсах, т.е. прихода любого вида стимула
var curActiveActionsPulsCount = 0 // время действия оператора
// образ предыдущего сосотояния Стимула ПОСЛЕ стимула Оператора (не меняется при активации изменением состояния)
var curStimulImage *ActionsImage
var curStimulImageID = 0

// Допустимое число пульсов после Стимула чтобы считать его ответом на действие
// ! НО УЖЕ ОПРЕДЕЛЕН ПЕРИОД ОЖИДАНИЯ ОТВЕТА - LastRunAutomatizmPulsCount, зачем еще этот??
var limitOfActionsAfterStimul = 5 //предел действий после стимула - не более 5 пульсов

// //////////////////////////////////////
var curActiveVerbalID = 0

// эффект от стимула Оператора
var globalStimulsEffect = 0
var prevStimulsEffect = 0 // предыдущий эффект

/*
	Нужна активаци новой Темы с ID==nn.

вызвать runNewTheme(needRunNewTheme,2)

	после активации дерева ситуации т.к. для дерева проблем нужен ID дерева ситуации
*/
var needRunNewTheme = 0

////////////////////////////////////////////////////////////////////////////////////////
/* попытка активации дерева автоматизмов, если неудачно - начать искать вариант действий
Используется активная текущая информационная среда из psychic.go:
var PsyBaseID=0 // текущее базовое состояние, может быть произвольно изменено
var PsyEmotionImg *Emotion // текущая эмоция Emotion, может быть произвольно изменена
var PsyActionImg *Activity // текущий образ сочетания действий с Пульта Activity
var PsyVerbImg *Verbal // текущий образ фразы с Пульта Verbal
*/
var detectedActiveLastNodID = 0

// последний detectedActiveLastNodID где была распознана фраза без isUnrecognizedPhraseFromAtmtzmTreeActivation==true
var lastWellPhrasedetectedActiveLastNodID = 0

// при запуске автоматизма по действию оператора, для Правил
var detectedActiveLastNodPrevID = 0
var detectedActiveLastUnderstandingNodPrevID = 0

// нераспознанный остаток - НОВИЗНА
var CurrentAutomatizTreeEnd []int
var currentStepCount = 0
var currentAutomatizmAfterTreeActivatedID = 0 //! это  не обязательно штатный автоматизм ветки, а выбранный мягким алгоритмом
var wasCurrentAutomatizmAfterTree = 0         // для активации функции consciousnessElementary: 1 этот автоматизм из текущей активации дерева, есть currentAutomatizmAfterTreeActivatedID

var curActivePhraseStr = ""
var isUnrecognizedPhraseFromAtmtzmTreeActivation = false //true - при активации была нераспознанная фраза

/*
	Детектор: активация дерева не вызвала автоматизм и не было периода ожидания.

только после действий оператора (а не активация по изменению гомео-параметров)
если ==2 - активация дерева не вызвала автоматизм и не было периода ожидания
*/
var NoautomatizmAfterStimul = 0

/////////////////////////////////

// активация дерева автоматизмов
func automatizmTreeActivation() int {

	if PulsCount < 4 { // не активировать пока все не устаканится
		return 0
	}

	if isRepressionStimulsNoise { //Подавление мешающих стимулов при серьезном поиске решений проблемы
		//контроль удержания режима isRepressionStimulsNoise
		checkRepressionStimulsNoises() // можно ли снять режим
	}

	if IsSleeping {
		return 0
	}
	/* НУЖНО, просто новый ор.рефлекс будет ждать окончания периода LastRunAutomatizmPulsCount
	if LastRunAutomatizmPulsCount >0{// не активировать в период ожидания результатов действий!
		return 0
	}
	*/

	/* ТЕПЕПЕРЬ ВСЕГДА АКТИВИРОВАТЬ потому как и по изменению состояния формируются Правила.
	   Но нужно блокировать ор.рефлексы!
	   // не активировать дерево по изменению гомеостатуса во время ожидания ответа оператора
	   //  LastRunAutomatizmPulsCount устанавливается в RumAutomatizm(
	   if LastRunAutomatizmPulsCount > 0{
	   if !WasOperatorActiveted {
	   	return 0
	   }
	*/

	curActiveTreePulsCount = PulsCount // время активации дерева автоматизмов, т.е. прихода любого вида стимула

	detectedActiveLastNodID = 0
	needRunNewTheme = 0

	ActiveBranchNodeArr = nil
	CurrentAutomatizTreeEnd = nil
	currentStepCount = 0
	currentAutomatizmAfterTreeActivatedID = 0
	isUnrecognizedPhraseFromAtmtzmTreeActivation = false

	// вытащить 3 уровня условий в виде ID их образов
	//Еще нет InformationEnvironment т.к. Дерево активруется ДО ор.рефлексов
	lev1 := gomeostas.CommonBadNormalWell
	oldCommonBadNormalWell = CurrentCommonBadNormalWell
	CurrentCommonBadNormalWell = lev1

	bsIDarr := gomeostas.GetCurContextActiveIDarr()
	lev2, emotion := createNewBaseStyle(0, bsIDarr, true)
	oldCurrentEmotionReception = CurrentEmotionReception
	CurrentEmotionReception = emotion

	/* Для дерева автоматизмов используютсЯ ID отдельных видов действий,
	но уже есть и общий ID стимула (который используется в дереве рефлексов)
	ActiveCurTriggerStimulsID - он испоьзуется в дереве эпиз.пяти как Стимул: EpisodicTreeNode.Trigger
	*/

	ActID := action_sensor.CheckCurActionsContext() //CheckCurActions()
	curActions.ActID = ActID
	lev3, _ := createNewlastActivityID(0, ActID, true) // текущий образ сочетания действий с Пульта Activity

	// дезактивировать все контексты!!!! чтобы не влияли на следующую активность
	action_sensor.DeactivationTriggersContext()
	//!!!!curActiveActionsID = 0
	//!!!!curActiveActions = nil

	var lev4 = 90 // нельзя 0, это ToneMood
	var lev5 = 0
	var lev6 = 0
	if len(wordSensor.CurrentPhrasesIDarr) > 0 {
		PhraseIDarr := wordSensor.CurrentPhrasesIDarr
		//Если CurrentPhrasesIDarr[n]==-1 - фраза есть, но она нераспознана.
		for i := 0; i < len(PhraseIDarr); i++ {
			if PhraseIDarr[i] == -1 { // фраза нераспознанна
				isUnrecognizedPhraseFromAtmtzmTreeActivation = true
			}
		}

		// нераспознанная фраза - все равно - наличие фразы, а не молчание и образуется нормальный CreateVerbalImage()

		FirstSimbolID := wordSensor.GetFirstSymbolFromPraseID(PhraseIDarr)
		ToneID := wordSensor.CurPultTone
		if ToneID == 0 && wordSensor.DetectedTone == 1 { // повышенный из-за знака "!"
			ToneID = 4 // 4- разница в кодировке тона
		}
		MoodID := wordSensor.CurPultMood
		verbID, verb := CreateVerbalImage(FirstSimbolID, PhraseIDarr, ToneID, MoodID)
		if verb != nil {
			lev4 = GetToneMoodID(verb.ToneID, verb.MoodID)
			lev5 = verb.SimbolID
			/* для дерева берется только первая фраза КАК ГЛАВНОЕ УСЛОВИЕ ВЕТКИ, остальные можно восстановить для сопоставлений из
			AutomatizmNode.PhraseID.PhraseIDarr[]
			*/
			lev6 = verb.ID
		}
		// в четвертой стадии обнулится в fixEpizMemoryTeachRules(). Если здесь обнулять, то в fixEpizMemoryTeachRules() пойдет фраза nil
		if EvolushnStage < 4 {
			// очистить фразу после использования, чтобы не влияла на следующую активность
			wordSensor.CurrentPhrasesIDarr = nil // остается еще wordSensor.CurretWordsIDarr
		}

		// сохраняем для отзеркаливания действий оператора
		curActiveVerbalID = verbID
		curActions.PhraseID = PhraseIDarr
		curActions.ToneID = ToneID
		curActions.MoodID = MoodID
	} else { // иначе при ответе ТОЛЬКО через действие, в правилах запишется фраза от предыдущего ответа, хотя на самом деле там не должно быть фразы вообще
		curActions.PhraseID = nil
		curActions.ToneID = 0
		curActions.MoodID = 0
		curActiveVerbalID = 0
	}

	if ActivationTypeSensor == 1 { // активация по изменению гомео-параметров
		/*	в understanding.go func consciousnessElementary()
			блокируются активаци при незначительных извенениях гомеопараметров if !gomeostas.GetGomeoParsDiff() {
			и если прошло меньше 10 пульсов со времени последнего стимула от оператора
		*/
		if isRepressionStimulsNoise { //Подавление мешающих стимулов при серьезном поиске решений проблемы
			// сторожевая функция определения критического состояния гомеостаза
			watchdogFunctionGomeo()
		}
	} else { // образ действий оператора

		// зафиксировать время появления Стимула и ждать запуска автоматизма 2 пульса, иначе detectedActiveLastNodID=2
		NoautomatizmAfterStimul = PulsCount // хоть и синоним curActiveActionsPulsCount
		// Внизу - после обработки clinerAutomatizmRunning() - новый симул сбрасывает ожидание ответа на старый

		// сохраняем предыдущий Стимул
		curStimulImage = curActiveActions
		curStimulImageID = curActiveActionsID

		curActiveActionsID, curActiveActions = CreateNewlastActionsImageID(0, 0, curActions.ActID, curActions.PhraseID, curActions.ToneID, curActions.MoodID, true)
		curActiveActionsPulsCount = PulsCount
		stimulCount++ // сколько раз был стимул от оператора после последнего запуска Ответа
		CurrentInformationEnvironment.ActionsImageID = curActiveActionsID

		if isRepressionStimulsNoise { //Подавление мешающих стимулов при серьезном поиске решений проблемы
			// сторожевая функция определения очень важного объекта внимания
			watchdogFunctionStimul(curActiveActions)
		}
	}

	/* Если данный curActiveActions содержит экстремальный объект с высокй значимостью,
	то это повлияет на базовые контексты, переактивировав их через func ContextActiveFromPsy()
	но не сразу, а консервативно с заданной степень консервативности.
	*/
	getExtremImportanceObject()
	if extremImportanceObject != nil && lib.Abs(extremImportanceObject.extremVal) > 5 {
		// переактивировать базовые контексты при conservatismPsyStymulEffect повторений такой значимости
		if gomeostas.ContextActiveFromPsy(extremImportanceObject.extremVal) { // была переактивация
			/* Посте такой переактивации будет опять запущена активация дерева func changingConditionsDetector()
			ну и пусть. А пока что пусть выполнится привычный автоматизм по прежнему условию.
			*/
			setInterruptionEpisosde() //вставить пустой кадр эпиз.памяти - прервать тему
			clinerAutomatizmRunning() // окончить период ожидания
		}
	}

	//Подавление мешающих стимулов при серьезном поиске решений проблемы if EvolushnStage > 4
	if isRepressionStimulsNoise {
		runIgnoreAction() // запустить действие Игнорировать
		return 0
	}

	condArr := getActiveConditionsArr(lev1, lev2, lev3, lev4, lev5, lev6)
	notAllowScanInTreeThisTime = true // защелка от повтора во время обработки
	// основа дерева
	cnt := len(AutomatizmTree.Children)
	for n := 0; n < cnt; n++ {
		node := AutomatizmTree.Children[n]
		lev1 := node.BaseID
		if condArr[0] == lev1 {
			detectedActiveLastNodID = node.ID
			ost := condArr[1:]
			if len(ost) == 0 {

			}

			conditionAutomatizmFound(1, ost, &node)

			break // другие ветки не смотреть
		}
	}

	// результат активации Дерева:
	if detectedActiveLastNodID > 0 {
		// есть ли еще неучтенные, нулевые условия? т.е. просто показаь число ненулевых значений condArr
		conditionsCount := getConditionsCount(condArr)
		CurrentAutomatizTreeEnd = condArr[currentStepCount:] // НОВИЗНА
		if currentStepCount < conditionsCount {              // не пройдено до конца имеющихся условий
			// нарастить недостающее в ветке дерева - всегда для orientation_1()
			//oldDetectedActiveLastNodID:=detectedActiveLastNodID
			detectedActiveLastNodID = formingBranch(detectedActiveLastNodID, currentStepCount, condArr)

			/* !!! если нераспознана фраза, то detectedActiveLastNodID НЕВЕРЕН его нельзя использовать как поиск по эп.памяти
			поэтому нужно использовать lastWellPhrasedetectedActiveLastNodID
			*/
			if !isUnrecognizedPhraseFromAtmtzmTreeActivation {
				// последний detectedActiveLastNodID где была распознана фраза без isUnrecognizedPhraseFromAtmtzmTreeActivation==true
				lastWellPhrasedetectedActiveLastNodID = detectedActiveLastNodID
			}
		}

	} else { // вообще нет совпадений для данных условий
		// нарастить недостающее в ветке дерева - всегда для orientation_1()
		detectedActiveLastNodID = formingBranch(detectedActiveLastNodID, currentStepCount, condArr)

		// автоматизма нет у недоделанной ветки
		CurrentAutomatizTreeEnd = condArr // все - новизна

	}

	if afterTreeActivation() {
		notAllowScanInTreeThisTime = false // снять блокировку
		return 1
	}
	notAllowScanInTreeThisTime = false // снять блокировку
	return 0
}

//////////////////////////////////////////////////////////////////

func conditionAutomatizmFound(level int, cond []int, node *AutomatizmNode) {
	if cond == nil || len(cond) == 0 {
		return
	}

	ost := cond[1:]

	for n := 0; n < len(node.Children); n++ {
		cld := node.Children[n]
		var val = 0
		switch level {
		case 0:
			val = cld.BaseID
		case 1:
			val = cld.EmotionID
		case 2:
			val = cld.ActivityID
		case 3:
			val = cld.ToneMoodID
		case 4:
			val = cld.SimbolID
		case 5:
			val = cld.PhraseID
		}
		if cond[0] == val {
			detectedActiveLastNodID = cld.ID
			ActiveBranchNodeArr = append(ActiveBranchNodeArr, cld.ID)
		} else {
			currentStepCount = level - 1
			continue
		}

		level++
		currentStepCount = level
		conditionAutomatizmFound(level, ost, &node.Children[n])
		return // раз совпало, то другие ветки не смотреть
	}

	return
}

////////////////////////////////////////////////////////

var onliOnceWasConditionsActiveted = false // т.к. опять может продолжиться изменение состояния в период ожидания
/*
	реакция после активации ветки дерева

если нет никаких действий, то возвращает false, инчае - true для блокировки более низкоуровневого
*/
func afterTreeActivation() bool {
	/* Нельзя здесь определять currentAutomatizmAfterTreeActivatedID перед if LastRunAutomatizmPulsCount >0{
	// ЕСТЬ ЛИ АВТОМАТИЗМ В ВЕТКЕ и болеее ранних? выбрать лучший автоматизм для сформированной ветки nodeID
	currentAutomatizmAfterTreeActivatedID = getAutomatizmFromNodeID(detectedActiveLastNodID)
	*/

	// по каждому действию оператора задумываться
	if ActivationTypeSensor > 1 { // есть инфа о curActions - параметры действий оператора
		needRunNewTheme = 11 //"Действие оператора"
	}

	/*

	 */

	//var wasRunUnderstandingSituation=false
	/*ПЕРИОД ОЖИДАНИЯ ОТВЕТА ОПЕРАТОРА, реагировать только на действия Оператора ActivationTypeSensor >1
	  	Был запущен моторный автоматизм (в том числе и ментальным автоматизмом)
	  Срабатывает при типе активации (ActivationTypeSensor>1) т.к. Правила записываются только
	  	со стимулом от Оператора и НЕ бывает со стимулом - по изменению состояния.
	*/

	if LastRunAutomatizmPulsCount > 0 && ActivationTypeSensor > 1 { //Обработка нового ответа оператора
		effect := 0
		// 	Контроль за изменением состояния, возвращает:
		//	lastCommonDiffValue - насколько изменилось общее состояние
		//  	lastBetterOrWorse - стали лучше или хуже: величина измнения от -10 через 0 до 10
		//  	gomeoParIdSuccesArr - стали лучше следующие г.параметры []int гоменостаза
		if WasOperatorActiveted { // оператор отреагировал
			lastCommonDiffValue, gomeoParIdSuccesArr := wasChangingMoodCondition() // здесь stimulsEffect
			/* обработать изменение состояния фиксация ПРАВИЛА, Стимул - ОТ ОПЕРАТОРА
			если эффектом действий кнопок заблокируется автоматизм, клонированный от рефлекса и повешенный на кнопку
			будет срабатывать его родитель-рефлекс и оказывать обучающее действие, через изменение состояния инфо-среды
			LastAutomatipmCorrection() отдельно отрабатывает эффект учительских кнопок Наказать/Поощрить,
			поэтому здесь это блокируем - иначе будет двойная оценка
			*/
			//if !action_sensor.IsPress3or4button {
			calcAutomatizmResult(lastCommonDiffValue, gomeoParIdSuccesArr)
			effect = lastCommonDiffValue
			/*} else {
				effect = effectPress3or4button
				effectPress3or4button = 0
			}*/
			// по результатам обработки, но до очистки 	LastRunAutomatizmPulsCount и LastAutomatizmWeiting
			if EvolushnStage > 3 {
				//	wasRunUnderstandingSituation=true
				/* Здесь не активировать Дерево пониманияситуации. Активировать только 1 раз (зачем 2 раза??)- внизу,
				   только после определения currentAutomatizmAfterTreeActivatedID
				   ведь будет вызов consciousnessElementary() - обработка ситуации с currentAutomatizmAfterTreeActivatedID !!!
				*/
				//understandingSituation(1)
				// !!!return true
			}
			//consciousnessThinking(8)

			// МЕНТАЛЬНЫЕ ПРАВИЛА после периода ожидания
			afterWaitingPeriod(effect) //Учесть последствия ментального запуска мот.автоматизма

			//и если нужно обдумать это
			/* позитивный эффект не обдумывать
			if effect >= 0 && curActiveActions.PhraseID !=nil && len(curActiveActions.PhraseID)>0{
				runNewTheme(11,1)
			}
			*/

			// закончить период ожидания после реакции оператора
			clinerAutomatizmRunning()
			WasConditionsActiveted = false // иначе сразу сработает fixRulesBaseStateImage после изменения состояния при действиях
		}

		/* Не записывать Правила по изменению состояния, а только - по стимулу от Оператора!
		   потому как состояние может улучшить только оператор, так что после его Стимула смотрим результат и оцениваем действие.
		   	if !onliOnceWasConditionsActiveted {// только один раз во время периода ожидания
		   		onliOnceWasConditionsActiveted = true
		   		if WasConditionsActiveted { // изменились условия (не действия оператора)
		   			WasConditionsActiveted = false
		   			if EvolushnStage > 3 {
		   				lastCommonDiffValue, _, _ := wasChangingMoodCondition(2)
			   // записать ПРАВИЛО типа BaseStateImage Стимул - НЕ ОТ ОПЕРАТОРА, а при активации изменением состояния
		   				fixRulesBaseStateImage(lastCommonDiffValue)// здесь корректируется успешность автоматизма - как в calcAutomatizmResult
		   				// Активировать Дерево Понимания: или запустить ментальный автоматизм или - ориентировочная реакция для осмысления
		   				understandingSituation(1)

		   				// НЕ заканчивать период ожидания после переактивации по изменившимся условиям, но не запускать ор.рефлекс:
		   				return true
		   			}
		   		}
		   	}*/

		// осмыслить результат, задав тему ThemeImage.Type=1
		if effect < 0 { // негативный эффект Стимула
			// -effect обычно большой и уже не перекрывается другой темой, так что задаем фиксированный 2
			//runNewTheme(2,2)
			needRunNewTheme = 2 //Негативный эффект ментального автоматизма
		}

		//  после обработки ожидаемой реакции Оператора - следует реакция Beast
		//		return true  поэтому нельзя здесь делать прерывание!
	}
	////////////////////////////// конец обработки ожидания ответа оператора

	/* ЕСТЬ ЛИ АВТОМАТИЗМ В ВЕТКЕ и болеее ранних? выбрать лучший автоматизм для сформированной ветки nodeID
		а если нет, то учитывать общие автомтизмы, привязанные к действиям (виртуальная ветка ID от 1000000) и словам (>2000000)

	Нужно учесть случай, что если на авторитарный ответ оператора вдруг срабатывает автоматизм,
		его и надо на пульт отправить. Иначе отправится сгенеренный зеркальный.
		Есть ведь ситуации, когда стимул новый, а ответ старый.
		Например, привет - хай, приветствую - хай. На два разных стимула может быть автоматизмы с одинаковыми действиями, но привязанные к разным узлам дерева.
		Без учета этого бот не сделает НОВЫЙ автоматизм, а просто выдаст старый.
	*/
	if oldAtmzAfterTreeActivatedID == 0 {

		currentAutomatizmAfterTreeActivatedID = getAutomatizmFromNodeID(detectedActiveLastNodID)

	} else {
		// ID автоматизма, активировашегося на стимул ДО создания зеркального в calcAutomatizmResult()
		currentAutomatizmAfterTreeActivatedID = oldAtmzAfterTreeActivatedID
		oldAtmzAfterTreeActivatedID = 0
	}
	wasCurrentAutomatizmAfterTree = 1 // для активации функции consciousnessElementary: 1 этот автоматизм из текущей активации дерева, есть currentAutomatizmAfterTreeActivatedID

	// всегда сначала активировать дерево понимания, результаты которого могут заблокировать все внизу
	//if !wasRunUnderstandingSituation && EvolushnStage > 3 {
	if EvolushnStage > 3 {

		// Активировать Дерево ситуации: или запустить ментальный автоматизм или - ориентировочная реакция для осмысления
		understandingSituation(1) //
		/*в func consciousnessElementary() был запущен моторный автоматизм, нужнозаблокировать все рефлексы и штатные автоматизмы,
		  может быть изменен штатный авмтоматизм и уже запущен другой.
		*/
		if MentalReasonBlocing && curFunc13ID == 0 {
			return true
		}
	}
	//////////////////////
	//более примитивное реагирование, EvolushnStage < 4
	if EvolushnStage < 4 {
		if currentAutomatizmAfterTreeActivatedID > 0 { //ориентировочный рефлекс 2
			// EvolushnStage < 4  проверить подходит ли автоматизм к текущим условиям, если нет, - режим нахождения альтернативы  - ориентировочный рефлекс 2
			atzm := orientation(currentAutomatizmAfterTreeActivatedID)
			// если автоматизм в orientation прошел проверку, то он сразу запущен
			if atzm > 0 { // блокировка рефлексов, если automatizmID > 0
				return true
			}
		} else {
			// автоматизма нет у недоделанной ветки
			atzm := orientation(0)
			if atzm > 0 { // блокировка рефлексов, если automatizmID > 0
				return true
			} else { // нет реакции
				if EvolushnStage < 4 {
					// это означает блокировку рефлексов, если его клон-автоматизм заблокирован. Так происходит простейшая коррекция наследственной модели поведения на уровне Да/Нет.
					// более тонкая настройка автоматизма и его разблокировка возможна на стадиях > 3
					lib.SentConfusion("Не смог сориентироваться.")
					return true
				}
			}
		}
	} //if EvolushnStage < 4 {

	if EvolushnStage > 3 {
		//- просто запустить штатный автоматизм, раз нет блокировки сознанием !MentalReasonBlocing
		if currentAutomatizmAfterTreeActivatedID > 0 {
			if curFunc13ID > 0 {
				/*  endBaseIdCycle(curFunc13ID) // подвисает
				clinerFunctionsInAllCickles(13)
				IsEndWaitPeriodFunc13 = true */
				curFunc13ID = 0 // закрываем маркер цикла func13
			}
			RumAutomatizmID(currentAutomatizmAfterTreeActivatedID)
		}
	}

	if EvolushnStage > 4 {
		// отзеркаливание авторитарных действий - как решение доминант по аналогии
		if existsBaseContext(2) || existsBaseContext(3) { // текущая эмоция - поиск или игра
			if lastAutomatizmRun != nil && lastAutomatizmRunPulsCount > PulsCount-20 {
				// оператор ответил на действия Beast не позже, чем за 20 сек
				checkRelevantAction(lastAutomatizmRun.ActionsImageID, curActions.ID, 2) // примем авторитарный эффект ==2
			}
		}
	}

	// не блокировать то, что не относится к текущей активации условий деревьями
	MentalReasonBlocing = false //true - заблокировать все низкоуровневое

	return false
}

//////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatism_tree_func.go
____________________________________________
/*  функции Дерева автоматизмов


 */

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/////////////////////////////////////////////////////////////

/*
	Создать новый узел дерева автоматизма.

Формат записи:
ID|ParentNode|BaseID|EmotionID|ActivityID|ToneMoodID|SimbolID|PhraseID
*/
var lastAutomatizmNodeID = 0
var noRunThisOperation = false // не проверять на дубли
func createNewAutomatizmNode(parent *AutomatizmNode, id int, baseID int, EmotionID int,
	ActivityID int, ToneMoodID int, SimbolID int, VerbalID int, CheckUnicum bool) (int, *AutomatizmNode) {

	if parent == nil {
		return 0, nil
	}

	//if !noRunThisOperation { НЕЛЬЗЯ ИГНОРИРОВАТЬ ИНАЧЕ СОЗДАЕТ ЛИШНЕЕ
	// если есть такой узел, то не создавать
	if CheckUnicum {
		idOld, nodeOld := FindAutomatizmTreeNodeFromCondition(baseID, EmotionID, ActivityID, ToneMoodID, SimbolID, VerbalID)
		if idOld > 0 {
			return idOld, nodeOld
		}
	}

	if id == 0 {
		lastAutomatizmNodeID++
		id = lastAutomatizmNodeID
	} else {
		//		newW.ID=id
		if lastAutomatizmNodeID < id {
			lastAutomatizmNodeID = id
		}
	}

	var node AutomatizmNode
	node.ID = id
	node.ParentNode = parent
	node.ParentID = parent.ID
	node.BaseID = baseID
	node.EmotionID = EmotionID
	node.ActivityID = ActivityID
	if ToneMoodID == 0 {
		//  !!!!! ToneMoodID=90
	}
	node.ToneMoodID = ToneMoodID
	node.SimbolID = SimbolID
	node.PhraseID = VerbalID

	parent.Children = append(parent.Children, node)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *AutomatizmNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
		}
	}

	//AutomatizmTreeFromID[node.ID]=&node
	WriteAutomatizmTreeFromID(node.ID, &node)

	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в AutomatizmTreeFromID:
	updateAutomatizmTreeFromID(parent) // здесь потому, что при загрузке из файла нужно на лету получать адреса

	return id, newN
}

// корректируем адреса всех узлов
func updateAutomatizmTreeFromID(parent *AutomatizmNode) {
	//updatingPhraseTreeFromID(&VernikePhraseTree)
	updatingPhraseTreeFromID(parent)
}

// проход всего дерева
func updatingPhraseTreeFromID(rt *AutomatizmNode) {
	if rt.ID > 0 {
		//rt.ParentNode=AutomatizmTreeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		node, ok := ReadeAutomatizmTreeFromID(rt.ParentID)
		if ok {
			rt.ParentNode = node
		}
		//AutomatizmTreeFromID[rt.ID] = rt
		WriteAutomatizmTreeFromID(rt.ID, rt)
	}
	if rt.Children == nil { // конец ветки
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		updatingPhraseTreeFromID(&rt.Children[i])
	}
}

///////////////////////////////////////////////////////////
/* загрузить записанное дерево
Формат записи:
ID|ParentNode|BaseID|EmotionID|ActivityID|ToneMoodID|SimbolID|PhraseID
*/
func loadAutomatizmTree() {
	//	createNulLevelAutomatizmTree(&AutomatizmTree)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/automatizm_tree.txt")
	cunt := len(strArr)
	AutomatizmTreeFromID = make([]*AutomatizmNode, cunt) //задать сразу имеющиеся в файле число

	//нулевой узел
	//AutomatizmTreeFromID[0]=&AutomatizmTree // все по нулям по умолчанию
	WriteAutomatizmTreeFromID(0, &AutomatizmTree)

	//просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		if len(strArr[n]) < 2 {
			panic("Сбой загрузки дерева рефлексов: [" + strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		parentID, _ := strconv.Atoi(p[1])

		baseID, _ := strconv.Atoi(p[2])
		EmotionID, _ := strconv.Atoi(p[3])
		ActivityID, _ := strconv.Atoi(p[4])
		ToneMoodID, _ := strconv.Atoi(p[5])
		SimbolID, _ := strconv.Atoi(p[6])
		VerbalID, _ := strconv.Atoi(p[7])
		// новый узел с каждой строкой из файла
		var saveDoWritingFile = doWritingFile
		doWritingFile = false

		node, ok := ReadeAutomatizmTreeFromID(parentID)
		if ok {
			createNewAutomatizmNode(node, id, baseID, EmotionID,
				ActivityID, ToneMoodID, SimbolID, VerbalID, false)
		}
		doWritingFile = saveDoWritingFile
	}

	// вытащить конечные узлы веток дерева, заполнить var lastnodsTreeArr =make(map[int]*AutomatizmNode)
	//finishScanAllTree()

	return
}

/*
// создать первый, нулевой уровень дерева
func createNulLevelAutomatizmTree(rt *AutomatizmNode){
	rt.ID=0
	AutomatizmTreeFromID[rt.ID]=rt
	return
}
*/
/////////////////////////////////////
func SaveAutomatizmTree() {
	notAllowScanInTreeThisTime = true
	var out = ""
	cnt := len(AutomatizmTree.Children)
	for n := 0; n < cnt; n++ { // чтобы записывалось по порядку родителей
		out += getAutomatizmNode(&AutomatizmTree.Children[n])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/automatizm_tree.txt", out)
	notAllowScanInTreeThisTime = false
	return
}
func getAutomatizmNode(wt *AutomatizmNode) string {
	var out = ""
	//	if wt.ParentID>0 {
	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|"
	out += strconv.Itoa(wt.BaseID) + "|"
	out += strconv.Itoa(wt.EmotionID) + "|"
	out += strconv.Itoa(wt.ActivityID) + "|"
	out += strconv.Itoa(wt.ToneMoodID) + "|"
	out += strconv.Itoa(wt.SimbolID) + "|"
	out += strconv.Itoa(wt.PhraseID)
	out += "\r\n"
	//	}
	if wt.Children == nil { // конец
		return out
	}
	for n := 0; n < len(wt.Children); n++ {
		out += getAutomatizmNode(&wt.Children[n])
	}
	return out
}

/////////////////////////////////////

// создать первые три ветки базовых состояний
func createBasicAutomatizmTree() {
	notAllowScanInTreeThisTime = true // запрет показа карты при обновлении

	createNewAutomatizmNode(&AutomatizmTree, 0, 1, 0, 0, 0, 0, 0, false)
	createNewAutomatizmNode(&AutomatizmTree, 0, 2, 0, 0, 0, 0, 0, false)
	createNewAutomatizmNode(&AutomatizmTree, 0, 3, 0, 0, 0, 0, 0, false)

	if doWritingFile {
		SaveAutomatizmTree()
	}
	//SaveAutomatizmTree()
	notAllowScanInTreeThisTime = false // запрет показа карты при обновлении
	return
}

/////////////////////////////////////////////////////

// найти КОНЕЧНЫЙ узел по условиям
func FindAutomatizmTreeNodeFromCondition(baseID int, EmotionID int,
	ActivityID int, ToneMoodID int, SimbolID int, VerbalID int) (int, *AutomatizmNode) {
	/*
		//	 поиск по дереву, что и эффективнее.
			for k, v := range AutomatizmTreeFromID {
			if v==nil{continue}
				if v.BaseID==baseID && v.EmotionID==EmotionID &&
					v.ActivityID==ActivityID && ToneMoodID==v.ToneMoodID && v.SimbolID==SimbolID && v.PhraseID==PhraseID{
					return k, v
				}
			}
			return 0,nil
	*/
	var id = 0
	var aut *AutomatizmNode
	cnt := len(AutomatizmTree.Children)
	for n := 0; n < cnt; n++ {
		id, aut = checkAutomatizmTree(&AutomatizmTree.Children[n], baseID, EmotionID, ActivityID, ToneMoodID, SimbolID, VerbalID)
		if id > 0 {
			return id, aut
		}
	}
	return 0, nil
}

// //////////////
func checkAutomatizmTree(v *AutomatizmNode, baseID int, EmotionID int,
	ActivityID int, ToneMoodID int, SimbolID int, VerbalID int) (int, *AutomatizmNode) {
	var id = v.ID
	var aut = v

	if v.ID == 13 {
		//	v.ID=13
	}
	// как только наткнется в предыдущих на такое услове - выдаст ID этой ветки
	if v.BaseID == baseID && v.EmotionID == EmotionID &&
		v.ActivityID == ActivityID && ToneMoodID == v.ToneMoodID && v.SimbolID == SimbolID && v.PhraseID == VerbalID {
		return v.ID, v
	}

	if v.Children == nil { // конец
		return 0, nil
	}
	for n := 0; n < len(v.Children); n++ {
		id, aut = checkAutomatizmTree(&v.Children[n], baseID, EmotionID, ActivityID, ToneMoodID, SimbolID, VerbalID)
		if id > 0 {
			return id, aut
		}
	}
	return 0, nil //v.ID

}

//////////////////////////////////////

//////////////////////////////////////////////////////////////
/* создание иерархии АКТИВНЫХ образов контекстов условий и пусковых стимулов в виде ID образов в [5]int
создать последовательность уровней условий в виде массива  ID последовательности ID уровней
*/
func getActiveConditionsArr(lev1 int, lev2 int, lev3 int, lev4 int, lev5 int, lev6 int) []int {
	arr := make([]int, 6)
	arr[0] = lev1
	arr[1] = lev2
	arr[2] = lev3
	arr[3] = lev4
	arr[4] = lev5
	arr[5] = lev6
	return arr
}
func getConditionsCount(condArr []int) int {
	var count = 0
	for i := 0; i < len(condArr); i++ {
		if condArr[i] > 0 {
			count++
		}
	}
	return count
}

////////////////////////////////////////////////////

// выдать массив узлов ветки по заданному ID узла
/*
func getBrangeNodeArr(lastNodeId int)([]*AutomatizmNode){
	var nArr []*AutomatizmNode

//	node:=AutomatizmTreeFromID[lastNodeId]
	node,ok:=ReadeAutomatizmTreeFromID(lastNodeId)
	if !ok {
		return nil
	}

	for {
		if node==nil {
			break
		}
		nArr = append(nArr, node)
		node=node.ParentNode
	}
	return nArr
}*/
//////////////////////////////////////

//////////////////////////////////////////////
/* поиск узла дерева автоматизмов по условиям у.рефлекса для automatizms_from_reflexes.go
Если нет такого узла - дорастить ветку.
Выдать  ID узла

*/
func FindConditionsNode(lev1 int, lev2 []int, actArr []int, tonMood int, fistSymb int, verbalID int) int {
	// образ эмоции
	eID, _ := createNewBaseStyle(0, lev2, true)
	// образ действий дерева: из TriggerStimuls -> Activity
	aID, _ := createNewlastActivityID(0, actArr, true) // конвертировать образ типа reflexes.TriggerStimuls в psychic.Activity

	// проход дераева автоматизмов:
	detectedActiveLastNodID = 0
	ActiveBranchNodeArr = nil
	CurrentAutomatizTreeEnd = nil
	currentStepCount = 0
	var lastNodeID = 0
	condArr := getActiveConditionsArr(lev1, eID, aID, tonMood, fistSymb, verbalID)
	if verbalID == 15 {
		//	verbalID=15
	}
	// основа дерева
	cnt := len(AutomatizmTree.Children)
	for n := 0; n < cnt; n++ {
		node := AutomatizmTree.Children[n]
		lev1 := node.BaseID
		if condArr[0] == lev1 {
			detectedActiveLastNodID = node.ID
			ost := condArr[1:]
			if len(ost) == 0 {

			}
			//conditionAutomatizmFound2(1,ost, &node)
			conditionAutomatizmFound(1, ost, &node)
			break // другие ветки не смотреть
		}
	}

	// результат активации Дерева:
	if detectedActiveLastNodID > 0 {
		// есть ли еще неучтенные, нулевые условия? т.е. просто показаь число ненулевых значений condArr
		conditionsCount := getConditionsCount(condArr)
		if currentStepCount < conditionsCount { // не пройдено до конца имеющихся условий
			noRunThisOperation = true // не проверять на дубли, раз уже проверено
			lastNodeID = formingBranch(detectedActiveLastNodID, currentStepCount, condArr)
			noRunThisOperation = false
		} else { // все condArr пойдены, ветка существует,
			// НО если последнее условие не совпадает, нужно создать ветку на основе предпоследнего узла
			//lenNcount:=len(ActiveBranchNodeArr)
			//if lenNcount<8{// остановилось на предпоследнем узле detectedActiveLastNodID

			//}

			return detectedActiveLastNodID
		}
	}

	/*
		// проверка
		n:=AutomatizmTreeFromID[lastNodeID];
		if n.PhraseID == 0{
			return 0
		}
	*/
	return lastNodeID
}

//.......
/*
func conditionAutomatizmFound2(level int,cond []int,node *AutomatizmNode){
	if cond==nil || len(cond)==0{
		return
	}

	ost:=cond[1:]

	if node.ID==6{
		node.ID=6
	}

	if level==4 && cond[0]==15{
		cond[0]=15
	}

	for n := 0; n < len(node.Children); n++ {
		cld := node.Children[n]
		var val = 0
		switch level {
		case 0:
			val = cld.BaseID
		case 1:
			val = cld.EmotionID
		case 2:
			val = cld.ActivityID
		case 3:
			val = cld.ToneMoodID
		case 4:
			val = cld.SimbolID
		case 5:
			val = cld.PhraseID
		}
		if cond[0] == val {
			detectedActiveLastNodID=cld.ID
			ActiveBranchNodeArr=append(ActiveBranchNodeArr,cld.ID)
		}else {
			currentStepCount=level-1
			return
			if level == 5 { // последний не совпадает


			}
		}

		level++
		currentStepCount=level
		conditionAutomatizmFound2(level,ost, &node.Children[n])
		return // раз совпало, то другие ветки не смотреть
	}

	return
}
*/
////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// создание ветки, начиная с заданного узла fromID
func formingBranch(fromID int, lastLevel int, condArr []int) int {
	// нарастить ветку недостающим

	//	lastNode:=AutomatizmTreeFromID[fromID]
	lastNode, ok := ReadeAutomatizmTreeFromID(fromID)
	if !ok {
		return 0
	}

	lastNodeID := addNewBranchFromNodes(lastLevel, condArr, lastNode)
	if lastNodeID > 0 {
		if !noRunThisOperation {
			if doWritingFile {
				SaveAutomatizmTree()
			}
		}
	}
	return lastNodeID
}

// ///////////////////////////////////////////////////
// создание новой ветки с новым автоматизмом, начиная с заданного узла при проходе дерева
func addNewBranchFromNodes(level int, cond []int, node *AutomatizmNode) int {
	if node == nil {
		return 0
	}
	if level >= len(cond) {
		return node.ID
	}
	var id = 0
	switch level {
	case 0:
		id, _ = createNewAutomatizmNode(node, 0, cond[0], 0, 0, 0, 0, 0, true)
	case 1:
		id, _ = createNewAutomatizmNode(node, 0, cond[0], cond[1], 0, 0, 0, 0, true)
	case 2:
		id, _ = createNewAutomatizmNode(node, 0, cond[0], cond[1], cond[2], 0, 0, 0, true)
	case 3:
		id, _ = createNewAutomatizmNode(node, 0, cond[0], cond[1], cond[2], cond[3], 0, 0, true)
	case 4:
		id, _ = createNewAutomatizmNode(node, 0, cond[0], cond[1], cond[2], cond[3], cond[4], 0, true)
	case 5:
		// нельзя отдельно указывать символ фразы и фразу - они всегда в паре - однако тогда не будет корректно работать категоризация по дереву getStimulCategoryChildrens()
		id, _ = createNewAutomatizmNode(node, 0, cond[0], cond[1], cond[2], cond[3], cond[4], cond[5], true)
	}
	level++

	node, ok := ReadeAutomatizmTreeFromID(id)
	if !ok {
		return 0
	}

	id = addNewBranchFromNodes(level, cond, node)
	return id
}

/////////////////////////////////////

// список конечных узлов лерева
/*
func finishScanAllTree(){
	lastnodsTreeArr =make(map[int]*AutomatizmNode)
	curScanAllTree(&AutomatizmTree)
}
func curScanAllTree(wt *AutomatizmNode) {
	if wt.ID > 0 {
		wt.ParentNode = AutomatizmTreeFromID[wt.ParentID] // wt.ParentNode адрес меняется из=за corretsParent(,
	}
	if wt.Children == nil {	// конец ветки
		lastnodsTreeArr[wt.ID]=wt
		return
	}
	for i := 0; i < len(wt.Children); i++ {
		curScanAllTree(&wt.Children[i])
	}
}*/
////////////////////////////////////////////////////////////

// вытащить образ Стимула ActiveActions из ID узла дерева автоматизмов
func getActiveActionsFromAutomatizmTreeNode(automatizmTreeNodeID int) (int, *ActionsImage) {

	//	at,ok:=AutomatizmTreeFromID[automatizmTreeNodeID]
	at, ok := ReadeAutomatizmTreeFromID(automatizmTreeNodeID)
	if !ok {
		return 0, nil
	}

	verb, ok := ReadeVerbalFromIdArr(at.PhraseID)
	if !ok {
		return 0, nil
	}
	t, m := GetToneMoodFromImg(at.ToneMoodID)

	return CreateNewlastActionsImageID(0, 0, []int{at.ActivityID}, verb.PhraseID, t, m, true)
}

///////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatism_tree_pult_actions_img.go
____________________________________________
/* образ сочетания действия с Пульта (кроме словестных) для 3-го уровня дерева автоматизмов.
Смысл (осознанную значимость) образ приобретеает только в контексте Дерева Понимания (дерева мент.автоматизмов)

Особенность: т.к. Activity обязательно присутствует в ветке дерева, а могут быть слова без предваряющего действия,
то первым формируется образ бездействия в createInactionImg()
и из этого узла будет формироваться основное количество вербальных веток.

Область моторных действий (для слов схожая область Брока) ActivityFromIdArr=make(map[int]*Activity)
отвечает за смысл распознанных действий с Пульта,
за конструирование собственных последовательностей действий,
за моторное использование действий.
За все ответственная структура - образ осмысленных действий и их сочетаний.
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)
//////////////////////////////////////////////



/////////////////////////////////////////////

func loadActivityInit(){

	loadActivityFromIdArr()

}
////////////////////////////////////////////

/*
для оптимизации поиска по дереву перед узлом Activity идет узел первого символа : var symbolsArr из word_tree.go

 */
type Activity struct {
	ID int
	ActID[] int // массив ID действий с Пульта 
}
var ActivityFromIdArr=make(map[int]*Activity)
//////////////////////////////////////////


// создать образ сочетаний пусковых стимулов
//В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0| - ID=2
var lastActivityID=0
func createNewlastActivityID(id int,ActID []int,CheckUnicum bool)(int,*Activity){
	if CheckUnicum {
		oldID,oldVal:=checkUnicumActivity(ActID)
		if oldVal!=nil{
			return oldID,oldVal
		}
	}

	if id==0{
		lastActivityID++
		id=lastActivityID
	}else{
		//		newW.ID=id
		if lastActivityID<id{
			lastActivityID=id
		}
	}

	var node Activity
	node.ID = id
	node.ActID = ActID

	ActivityFromIdArr[id]=&node
	return id,&node
}
func checkUnicumActivity(ActID []int)(int,*Activity){
	for id, v := range ActivityFromIdArr {
		if v==nil || !lib.EqualArrs(ActID,v.ActID) {
			continue
		}
		return id,v
	}

	return 0,nil
}
/////////////////////////////////////////
// создать новый образ сочетаний действий, если такого еще нет
func CreateNewActivityImage(ActID []int)(int,*Activity){
	if ActID==nil{
		return 0,nil
	}

	id,verb:=createNewlastActivityID(0,ActID,true)

	if doWritingFile {SaveActivityFromIdArr() }

	return id,verb
}

/////////////////////////////////////////

//////////////////// сохранить образы сочетаний пусковых стимулов
//В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0|
func SaveActivityFromIdArr(){
	var out=""
	for k, v := range ActivityFromIdArr {
	if v==nil{continue}
		out+=strconv.Itoa(k)+"|"
		for i := 0; i < len(v.ActID); i++ {
			out+=strconv.Itoa(v.ActID[i])+","
		}
		out+="|"
		out+="\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/activity_images.txt",out)

}
////////////////////  загрузить образы сочетаний пусковых стимулов
func loadActivityFromIdArr(){
	ActivityFromIdArr=make(map[int]*Activity)
	// сразу создать первым образ бездействий с Пульта - самое частое состояние образов действия
	createInactionImg()
	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/activity_images.txt")
	cunt:=len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		p:=strings.Split(strArr[n], "|")
		id,_:=strconv.Atoi(p[0])
		s:=strings.Split(p[1], ",")
		var ActID[] int
		for i := 0; i < len(s); i++ {
			if len(s[i])==0{
				continue
			}
			si,_:=strconv.Atoi(s[i])
			ActID=append(ActID,si)
		}
var saveDoWritingFile= doWritingFile; doWritingFile =false
		createNewlastActivityID(id,ActID,false)
doWritingFile =saveDoWritingFile
	}
	return

}
//// создать первым образ бездействий с Пульта - самое частое состояние образов действия
func createInactionImg(){
	createNewlastActivityID(1,[]int{0},true)
}
///////////////////////////////////////////



____________________________________________

BOT/brain/psychic/automatism_tree_pult_show.go
____________________________________________
/*  Выдать на Пульт дерево автоматизмов

 */

package psychic

import (
	actionSensor "BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
)

////////////////////////////////////////////

/*
запрет показа карты на пульте (func GetAutomatizmTreeForPult()) при обновлении
против паники типа "одновременная запись и считывание карты"
Использовать для всех операций записи узлов дерева
*/
var notAllowScanInTreeThisTime = false

// ограничение показа
var baseConditionIdOnly = 0 // 1- только Плохо, ...

// образ дерева автоматизмов для вывода
var automatizmTreeModel = ""

// ///////////////////////////////////////////////
func GetAutomatizmTreeForPult(limitBasicID int) string {

	automatizmTreeModel = "" // иначе дублирует блоки дерева при каждом выводе страницы
	// против паники типа "одновременная запись и считывание карты"
	if notAllowScanInTreeThisTime {
		return "!Временно запрещена работа func GetAutomatizmTreeForPult() т.к. идет параллельная обработка."
	}
	if len(AutomatizmTree.Children) == 0 { // еще нет никаких веток
		return "Еще нет Дерева автоматизмов"
	}

	//посмотреть число имеющихся узлов дерева
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/automatizm_tree.txt")
	cunt := len(strArr)
	// если больше 1000 то выдавать только по одному из 3-х базовыз состояний, иначе сильно тормозит
	if cunt > 1000 {
		if limitBasicID == 0 {
			limitBasicID = 1 // начинать с Плохо
		}
	}
	// переключатель диапазона вывода
	if limitBasicID > 0 {
		var out = ""
		out += "<br>Показывать: "
		out += "<span style='cursor:pointer;color:blue"
		if limitBasicID == 1 {
			out += "background-color:#FFFF9D;font-weight:bold;"
		}
		out += "' onClick='show_level(1)'>Плохо</span> "

		out += "<span style='cursor:pointer;color:blue"
		if limitBasicID == 2 {
			out += "background-color:#FFFF9D;font-weight:bold;"
		}
		out += "' onClick='show_level(2)'>Норма</span> "

		out += "<span style='cursor:pointer;color:blue"
		if limitBasicID == 3 {
			out += "background-color:#FFFF9D;font-weight:bold;"
		}
		out += "' onClick='show_level(3)'>Хорошо</span> "

		out += "<span style='padding-left:100px'></span>Автоматизмы узлов показываются по клику на АВТОМАТИЗМЫ<hr>"

		automatizmTreeModel = out
		baseConditionIdOnly = limitBasicID
	}
	//	automatizmTreeModel="" // иначе дублирует блоки дерева при каждом выводе страницы
	scanAutomatizmNodes(-1, &AutomatizmTree)

	if len(automatizmTreeModel) < 10 {
		return "Еще нет информационных веток дерева"
	}

	return automatizmTreeModel

}

//////////////////////

func scanAutomatizmNodes(level int, node *AutomatizmNode) {

	if node.ID == 69 {
		node.ID = 69
	}
	if node.ID > 0 {
		automatizmTreeModel += setShift(level)
		switch level {
		case 0:
			automatizmTreeModel += getStrFromCond(level, node.BaseID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 1:
			automatizmTreeModel += getStrFromCond(level, node.EmotionID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 2:
			automatizmTreeModel += getStrFromCond(level, node.ActivityID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 3:
			automatizmTreeModel += getStrFromCond(level, node.ToneMoodID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 4:
			automatizmTreeModel += getStrFromCond(level, node.SimbolID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 5:
			automatizmTreeModel += getStrFromCond(level, node.PhraseID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"

		}

		// если есть штатный автоматизм - показать действия
		/*
			atmzm:=AutomatizmBelief2FromTreeNodeId[node.ID]
			if atmzm!=nil{
				automatizmTreeModel += " <span style='color:blue'>АВТОМАТИЗМ(" + strconv.Itoa(atmzm.ID) + "): "+
					TranslateAutomatizmSequence(atmzm) + "</span>"
			}
		*/
		//автоматизмы, прикрепленные к ID узла Дерева

		atmzm := GetMotorsAutomatizmListFromTreeId(node.ID)

		if atmzm != nil {
			var autStr = "ID: "
			for i := 0; i < len(atmzm); i++ {
				if i > 0 {
					autStr += ", "
				}
				autStr += "" + strconv.Itoa(atmzm[i].ID)
			}
			automatizmTreeModel += " <span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(node.ID) + ")'>АВТОМАТИЗМЫ(" + autStr + "): " + "</span>"
		}
		automatizmTreeModel += "<br>\n"
	}
	level++
	for n := 0; n < len(node.Children); n++ {
		if baseConditionIdOnly > 0 {
			if node.BaseID > 0 && node.BaseID != baseConditionIdOnly {
				continue
			}
		}
		scanAutomatizmNodes(level, &node.Children[n])
	}
}

// отступ
func setShift(level int) string {
	var sh = ""
	for n := 0; n < level; n++ {
		sh += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	}
	return sh
}

////////////////////////////////////////////////////

// ///////////////////////////////////////////////////
// из ID образа получить составляющие в виде строк
func getStrFromCond(level int, imgID int) string {
	var out = ""
	switch level {
	case 0:
		if imgID > 0 && imgID < 4 {
			out = "Состояние: <b>" + gomeostas.GetBaseCondFromID(imgID) + "</b>"
		} else {
			out += "<span style='color:red'>несуществующее Базовое состояние ID=" + strconv.Itoa(imgID) + "</span>"
		}
	case 1: // эмоция
		out = "Эмоция (" + strconv.Itoa(imgID) + "): <b>" + GetStrnameFromBaseImageID(imgID) + "</b>"
	case 2: // действия
		out = getStrnameFromStyleImageID(imgID)
		if len(out) == 0 {
			return "Нет действий с Пульта "
		} else {
			out = "Действия с Пульта: <b>" + out + "</b>"
		}
	case 3: // тон-настроение фразы
		out = GetToneMoodStrFromID(imgID) // getToneStrFromID(imgID)
		if len(out) == 0 {
			return "Нормальное настроение"
		} else {
			out = "Тон-Настроение: <b>" + out + "</b>"
		}
	case 4: // первый символ
		out = word_sensor.GetSynbolFromID(imgID)
		if len(out) == 0 || out == " " {
			return "Нет первого символа фразы</b>"
		} else {
			out = "Первый символ: <b>&quot;" + out + "&quot;</b>"
		}
	case 5: // фраза
		//vrbal:=VerbalFromIdArr[imgID]
		//if vrbal != nil {
		//out = word_sensor.GetPhraseStringsFromPhraseID(vrbal.PhraseID[0])
		out = GetPraseStringsFromVerbalID(imgID) //word_sensor.GetPhraseStringsFromPhraseID(imgID)
		if len(out) == 0 {
			return "Нет фразы"
		} else {
			out = "Фраза: <b>&quot;" + out + "&quot;</b>"
		}
		//}
	}
	return out
}

// названия базовых контекстов в их сочетании -из ID эмоции
func GetStrnameFromBaseImageID(id int) string {
	var out = ""
	if EmotionFromIdArr[id] == nil {
		return "Нет эмоций"
	}
	img := EmotionFromIdArr[id].BaseIDarr
	for i := 0; i < len(img); i++ {
		if i > 0 {
			out += ", "
		}
		name := gomeostas.GetBaseContextCondFromID(img[i]) + ""
		out += name
	}
	if len(out) == 0 {
		return "Нет эмоций"
	}
	return out
}

// названия Пусковых стимулов в их сочетании -из ID их образа
func getStrnameFromStyleImageID(id int) string {
	if ActivityFromIdArr[id] == nil {
		return "Нет действий с Пульта"
	}
	var out = ""
	img := ActivityFromIdArr[id].ActID
	for i := 0; i < len(img); i++ {
		if i > 0 {
			out += ", "
		}
		name := actionSensor.GetActionNameFromID(img[i]) + ""
		out += name
	}
	if len(out) == 0 {
		return "Нет действий с Пульта"
	}
	return out
}

/////////////////////////////////////////////

/////////////////////////////////////////////
/*расшифровать действия автоматизма для инфы пульта: Snn:21812,27777,0,1478,13388,0,27303,24882Dnn:4
Сделано на основе запуска автоматизма на выполнение: func RumAutomatizmID(id int) из automatizm_actions.go
*/
func TranslateAutomatizmSequence(am *Automatizm) string {
	if am == nil {
		return ""
	}
	if am.ActionsImageID == 0 {
		return ""
	}

	out := GetAutomatizmSequenceInfo(am.ID, true)

	return out
}

////////////////////////////////////////

// действия - в виде строки
func GetAutomatizmSequenceInfo(idA int, writeLog bool) string {
	//am:= AutomatizmFromId[idA]
	am, ok := ReadeAutomatizmFromId(idA)
	if !ok {
		return ""
	}
	out := GetAutomotizmActionsString(am, writeLog, true)
	return out
}

///////////////////////////////////////////////////

// автоматизмы, привязанные к данному узлу дерева
func GetAutomatizmForNodeInfo(nodeID int) string {
	var out = ""

	atmzm := GetMotorsAutomatizmListFromTreeId(nodeID)

	if atmzm != nil {
		for i := 0; i < len(atmzm); i++ {
			if i > 0 {
				out += "<hr>"
			}
			out += "Автоматизм ID=" + strconv.Itoa(atmzm[i].ID) + ":<br>" + TranslateAutomatizmSequence(atmzm[i])
		}
	} else {
		out += "Нет автоматизмов, привязанных к узлу с ID=" + strconv.Itoa(nodeID)
	}
	return out
}

func GetNodesAutomatismsInfo(nodeID int) string {
	var out = ""

	atmzm := GetMotorsAutomatizmListFromTreeId(nodeID)

	if atmzm != nil {
		for i := 0; i < len(atmzm); i++ {
			if i > 0 {
				out += "<hr>"
			}
			out += "Автоматизм ID=" + strconv.Itoa(atmzm[i].ID) + ":<br>" + GetAutomotizmActionsString(atmzm[i], false, true)
		}
	} else {
		out += "Нет автоматизмов, привязанных к узлу с ID=" + strconv.Itoa(nodeID)
	}
	return out

}

// короткая инфа об узле дерева автоматизмов
func GetAutomatizmNodeTreeForPult(id int) string {

	node := AutomatizmTreeFromID[id]
	out := ""
	if node == nil {
		out += "<span style='color:red'>Нет узла дерева авт-м с ID=" + strconv.Itoa(id) + "</span>"
	} else {
		out += "Узел дерева автоматизмов ID=" + strconv.Itoa(id) + ":<br>"
		moodS := ""
		switch node.BaseID {
		case 1:
			moodS = "Плохо"
		case 2:
			moodS = "Норма"
		case 3:
			moodS = "Хорошо"
		}
		out += "Состояние: <b> " + moodS + "</b>"

		out += getStrFromCond(1, node.EmotionID)
		out += getStrFromCond(2, node.ActivityID)
		out += getStrFromCond(3, node.ToneMoodID)
		out += getStrFromCond(4, node.SimbolID)
		out += getStrFromCond(5, node.PhraseID)

		out += "<hr>"
		//автоматизмы, прикрепленные к ID узла Дерева
		atmzm := GetMotorsAutomatizmListFromTreeId(id)
		if atmzm != nil {
			var autStr = "ID: "
			for i := 0; i < len(atmzm); i++ {
				if i > 0 {
					autStr += ", "
				}
				autStr += "" + strconv.Itoa(atmzm[i].ID)
			}
			out += " <span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(node.ID) + ")'>АВТОМАТИЗМЫ(" + autStr + "): " + "</span>"
		}
	}
	return out
}

____________________________________________

BOT/brain/psychic/automatizm.go
____________________________________________
/* Автоматизмы, могут совершать внешние действия или внутренние произвольные действия.
К ветке дерева может быть прикреплено сколько угодно автоматизмов: GetMotorsAutomatizmListFromTreeId(branchID)
но только один из автоматизмов, прикрепленных к ветке, может иметь Belief=2 - проверенное собственное знание
Автоматизмы могут быть и не привязаны к конкретной ветке дерева, а быть привязаны к отдельным значениям AutomatizmNode:
- к ID образа действий с пульта ActivityID и тогда branchID начинается с 1000000,
сохраняются в карте AutomatizmIdFromActionId
- к ID фразы PhraseID  и тогда branchID начинается с 2000000,
сохраняются в карте AutomatizmIdFromPhraseId

Если задается Belief=2, остальные Belief=2 становится Belief=0.
!!! ПОЭТОМУ ВСЕГДА нужно задавать с помощью SetAutomatizmBelief(atmzm *Automatizm,belief int))

Если для прикрепленных к узлу дерева есть карта штатных AutomatizmBelief2FromTreeNodeId,
то для прикрепленных к образам нужны ФУНКЦИИ ПОЛУЧЕНИЯ ШТАТНОГО ДЛЯ ДАННОГО ОБРАЗА:
func GetAutomatizmBeliefFromActionId(activityID int)(*Automatizm){
func GetAutomatizmBeliefFromPhraseId(verbalID int)(*Automatizm){

Концепция общих автоматизмов. Они сформированы на основе общего шаблона рефлексов и, как и рефлексы, перекрываются автоматизмами конца активной ветки, т.е. имеющими образ Стимула. Это – первичная реакция на текущее сочетание контекстов (в данном случае – на эмоцию). У узла эмоции может быть бесконечное число образов действия и блокировка общего автоматизма лишает первичной реакции у всех их. Но у.рефлекс может быть заморожен, а общий автоматизм сейчас – нет.
Это значит, то в случае блокирующих действий для данной ветки, необходимо запускать автоматизм бездействия, останавливающий все более низкоуровневое.
При блокировке такого автоматизма опять НЕ БЛОКРУЕТСЯ.
Наличие игнор.автоматизма в конце ветки для func consciousnessElementary() равноценно отсуствю автоматизма.

Структура записи: id|BranchID|Usefulness|ActionsImageID|NextID|Energy|Belief|motAutmtzmID
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

// инициализирующий блок - в порядке последовательности инициализаций
// вызывается из psychic.go
func automatizmInit(){
	loadAutomatizm()
	//res:=RumAutomatizm(AutomatizmFromId[1])
	//if res{}
}

type Automatizm struct {
	ID	int
/* id объекта к кторому привязан автоматизм:
(он может быть  привязан к узлу дерева, к фразе (AutomatizmIdFromPhraseId) или действиям (AutomatizmIdFromActionId)
т.е. втоматизмы могут быть и не привязаны к конкретному узлу ветки дерева,
а быть привязаны к отдельным значениям AutomatizmNode:
   к ID образа действий с пульта ActivityID и тогда branchID начинается с 1000000,
сохраняются в карте AutomatizmIdFromActionId
   к ID фразы PhraseID  и тогда branchID начинается с 2000000,
сохраняются в карте AutomatizmIdFromPhraseId

Если бы автоматизм не привязывался к ветке через BranchID, а чтобы была карта для ветки с привязанными автоматизмами.
Тогда один и тот же автоматизм мог бы быть привязан к любым веткам.
Но тогда он бы утратил специфику NextID, полезности и повторяемости для данной ветки.
Хотя можно было бы сделать структуру для каждой ветки для привязки автоматизмов и учета их специфики, но тогда автоматизм просто вырождается в действие и становится не нужным.
*/
	BranchID   int
	//TODO: достаточно -1, 0, +1. В паре с Count это даст величину градации (бес)полезности автоматизма
	Usefulness int // (БЕС)ПОЛЕЗНОСТЬ: -10 вред 0 +10 +n польза
	// образ действий типа ActionsImage
	ActionsImageID int

	/* Next - следующая цепочка действий типа AmtzmNextString:
	т.е. последовательность действий ActionsImageID, начинающаяся с AmtzmNextString.ID.
	Цепь может быть пройдена ментально, без выполнения автоматизмов,
	для этого не вызывается моторное выполнение а просто - проход цепочки с просмотром ее звеньев.
	или цепь может быть прервана осознанно.
	*/
	NextID int

/* Энергичность действия или фразы.
   Но т.к. автоматизм может использоваться в разных случаях,
   лучше для этих конкретных случаев использования уточнять энергичность
   с помощью мозжечковых рефлексов.
Для NextID Energy наследуется от родителя и поэтому к Next не применяется мозжечковый рефлекс.
 */
	Energy int // от 1 до 10, по умолчанию = 5
	/* Уверенность в авторитарном автоматизме высока в период авторитарного обучения
	и сильно падает в период собственной инициативы, когда нужно на себе проверить,
	а даст ли такой автоматизм в самом деле обещанное улучшение.
	Только один из автоматизмов, прикрепленных к ветке, может иметь Belief=2 - проверенное собственное знание
	Если задается Belief=2, остальные Belief=2 становится Belief=0.
!!! ПОЭТОМУ ВСЕГДА нужно задавать с помощью SetAutomatizmBelief(atmzm *Automatizm,belief int)
	*/
	Belief int // 0 - предположение, 1 - чужие сведения, 2 - проверенное собственное знание
	/* В случае, если в результате автоматизма его Usefulness изменит знак, то
	motAutmtzmID обнулится, а при таком же знаке - увеличивается на 1.
	*/
	Count int // надежность: число использований с подтверждением (бес)полезности Usefulness
	/* какие ID гомео-параметров улучшает это действие
	по аналогии и функциональности с http://go/pages/terminal_actions.php
	*/
	GomeoIdSuccesArr []int
}

// все, привязанные к узлу дерева или привязанные к id образа действия и к id фразы.
//var AutomatizmFromId = make(map[int]*Automatizm)
var AutomatizmFromId []*Automatizm
// запись члена
func WriteAutomatizmFromId(index int, value *Automatizm) {
	if index >= len(AutomatizmFromId) {
		newSlice := make([]*Automatizm, index+1)
		copy(newSlice, AutomatizmFromId)
		AutomatizmFromId = newSlice
	}
	AutomatizmFromId[index] = value
}
// считывание члена
func ReadeAutomatizmFromId(index int) (*Automatizm,bool){
	if index >= len(AutomatizmFromId) || AutomatizmFromId[index]==nil {
		return nil,false
	}
	return AutomatizmFromId[index],true
}
///////////////////////////////////////

// ШТАТНЫЕ автоматизмы, прикрепленные к ID узла Дерева с Belief==2 т.е. ШТАТНЫЕ, выполняющиеся не раздумывая
// у узла может быть только один штатный автоматизм с Belief==2
var AutomatizmBelief2FromTreeNodeId = make(map[int]*Automatizm)
// ЭТОТ ПУСТЬ БУДЕТ НА КАРТЕ

/////////////////////////////////////////////////////////////////
//привязанные к ID образа действий с пульта ActivityID и тогда их branchID начинается с 1000000
// среди привязанный к данному образуID может быть один штатный с Belief==2
var AutomatizmIdFromActionId = make(map[int] []*Automatizm)
//привязанные к ID фразы PhraseID и тогда их branchID начинается с 2000000
// среди привязанных к данной фразеID (неважны предыдущие условия) может быть один штатный с Belief==2
var AutomatizmIdFromPhraseId = make(map[int] []*Automatizm)

/* список удачных автоматизмов, относящихся к определенным условиям (привзяанных к определенной ветке Дерева)
В этом списке поле Usefulness >0
 */
var AutomatizmSuccessFromIdArr = make(map[int]*Automatizm)

// GetMotorsAutomatizmListFromTreeId(nodeID int) список всех автоматизмов для ID узла Дерева
// ExistsAutomatizmForThisNodeID(nodeID int) есть ли штатный автоматизм (с Belief==2), привязанные к узлу дерева
// GetBelief2AutomatizmListFromTreeId(nodeID int) штатный, невредный автоматизм, привязанный к ветке


var lastAutomatizmID = 0 // ID последнего созданного автоматизма
var NoWarningCreateShow = false // true - не выдавать сообщение о новом автоматизме

// удалить автоматизм
func deleteAutomatizm(a *Automatizm){
	delete(AutomatizmBelief2FromTreeNodeId, a.ID)

//	delete(AutomatizmFromId, a.ID)
	WriteAutomatizmFromId(a.ID, nil)

	a=nil
}
////////////////////////////////////////////

/* создать новый автоматизм
checkLevel - глубина проверки на идентичность: 0 - нет проверки, 1 - поверхностная, 2 - полная
 */
func createNewAutomatizmID(id int,BranchID int,ActionsImageID int,CheckUnicum bool)(int,*Automatizm) {
/* Автоматизмы уникальны по сочетанию BranchID и ActionsImageID.
	При попытке создать с таким же сочетанием возвращается уже созданный.
 к одной вентке могут быть прикреплены неограниченное число автоматизмов
 */

	// не создавать автоматизм с нулевым образом - такое может быть при новом слове и отключенной галке Форсажа
	if ActionsImageID==0 {
		return 0, nil
	}
	if CheckUnicum {
		oldID, oldVal := checkUnicumMotorsAutomatizm(BranchID, ActionsImageID)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastAutomatizmID++
		id=lastAutomatizmID
	} else {
		if lastAutomatizmID < id {
			lastAutomatizmID = id
		}
	}

	var node Automatizm
	node.ID = id
	node.BranchID = BranchID
	node.Energy = 5
	node.ActionsImageID = ActionsImageID

//	AutomatizmFromId[id] = &node
	WriteAutomatizmFromId(id, &node)

	if BranchID > 1000000 && BranchID < 2000000 {
		imgID := BranchID - 1000000
		AutomatizmIdFromActionId[imgID] = append(AutomatizmIdFromActionId[imgID], &node)
	}
	if BranchID > 2000000 {
		imgID := BranchID - 2000000
		AutomatizmIdFromPhraseId[imgID] = append(AutomatizmIdFromPhraseId[imgID], &node)
	}

	if !NoWarningCreateShow {
		lib.WritePultConsol("Создан новый автоматизм.")
	}
	return id, &node
}

/* Автоматизмы уникальны по сочетанию BranchID и ActionsImageID.
Функцию можно использовать для выборки автоматизма с заданными BranchID и ActionsImageID
*/
func checkUnicumMotorsAutomatizm(BranchID int,ActionsImageID int)(int,*Automatizm){
if AutomatizmFromId==nil{
	return 0,nil
}

	for id, v := range AutomatizmFromId {
		if v==nil || (BranchID != v.BranchID || ActionsImageID != v.ActionsImageID) {
			continue
		}

		return id,v
	}

	return 0,nil
}

// создать новый автоматизм 
func CreateNewAutomatizm(BranchID int, ActionsImageID int)(int, *Automatizm) {
	// BranchID может быть ==0 для мозжечковых рефлексов
	if ActionsImageID == 0 { return 0, nil }

	id, verb := createNewAutomatizmID(0, BranchID, ActionsImageID,true)
	
	if doWritingFile {SaveAutomatizm() }

	return id, verb
}


/* создать дубликат автоматизма по образу имеющегося am *Automatizm
т.е. неуникальный по привязке к ветке дерева и действию - для пополнения коллекции автоматизмов ветки.
Полезность и коунтер - начальные (==0).
*/
func createDuplicateAutomatizm(BranchID int, am *Automatizm)(int, *Automatizm) {
	id, am := createNewAutomatizmID(0,BranchID,am.ActionsImageID,false)
	return id, am
}

// создать новый автоматизм без записи в файл
func CreateAtutomatizmNoSaveFile(BranchID int, ActionsImageID int)(int, *Automatizm) {
	// BranchID может быть ==0 для мозжечковых рефлексов
	if ActionsImageID == 0 {	return 0, nil }

	id, verb := createNewAutomatizmID(0, BranchID, ActionsImageID,true)

	return id,verb
}

// СОХРАНИТЬ структура записи: id|BranchID|Usefulness|ActionsImageID|NextID|Energy|Belief
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0|
func SaveAutomatizm() {
	var out = ""
	for k, v := range AutomatizmFromId {
		if v==nil{
			continue
		}
		out += strconv.Itoa(k) + "|"
		out += strconv.Itoa(v.BranchID) + "|"
		out += strconv.Itoa(v.Usefulness) + "|"
		out += strconv.Itoa(v.ActionsImageID) + "|"
		out += strconv.Itoa(v.NextID) + "|"
		out += strconv.Itoa(v.Energy) + "|"
		out += strconv.Itoa(v.Belief) + "|"
		out += strconv.Itoa(v.Count) + "|"
		for i := 0; i < len(v.GomeoIdSuccesArr); i++ {
			out += strconv.Itoa(v.GomeoIdSuccesArr[i]) + ","
		}
		out += "\r\n"
	}

	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_psy/automatizm_images.txt", out)
}

// ЗАГРУЗИТЬ структура записи: id|BranchID|Usefulness||ActionsImageID||NextID|Energy|Belief
func loadAutomatizm() {
	NoWarningCreateShow = true


	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/automatizm_images.txt")
	if strArr == nil { return	}
	//AutomatizmFromId = make(map[int]*Automatizm)
	AutomatizmFromId = make([]*Automatizm, len(strArr))//задать сразу имеющиеся в файле число
	for n := 0; n < len(strArr); n++ {
		if len(strArr[n]) == 0 { continue	}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		BranchID, _ := strconv.Atoi(p[1])
		Usefulness, _ := strconv.Atoi(p[2])
		ActionsImageID, _ := strconv.Atoi(p[3])
		NextID, _ := strconv.Atoi(p[4])
		Energy, _ := strconv.Atoi(p[5])
		Belief, _ := strconv.Atoi(p[6])
		Count, _ := strconv.Atoi(p[7])
		s := strings.Split(p[4], ",")
		var GomeoIdSuccesArr[] int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 { continue }
			sp, _ := strconv.Atoi(s[i])
			GomeoIdSuccesArr = append(GomeoIdSuccesArr, sp)
		}
var saveDoWritingFile= doWritingFile; doWritingFile =false
		_, a := createNewAutomatizmID(id, BranchID, ActionsImageID,false)// без проверки на уникальность
		a.NextID = NextID
		a.Usefulness = Usefulness
		a.Energy = Energy
		a.Count = Count
		SetAutomatizmBelief(a, Belief)
doWritingFile =saveDoWritingFile
	}
	NoWarningCreateShow = false
	return
}
/////////////////////////////////////////////////////////////
____________________________________________

BOT/brain/psychic/automatizm_actions.go
____________________________________________
/*  Моторные дейсвтвия автоматизма

Для каждого действия brain\reflexes\terminete_action.go задается "сила" действия в градации от 1 до 10, которая передается наПульт словами:
Максимально (сила=10), wwww (сила=8)", "Очень сильно (сила=9), ... Едва (сила=1).
При этом пропорционально расходуется энергия и могут происходить другие изменения гоместаза.
Такой результат сопоставляется с допустимым сразу при действии и корректируется установкой рефлекса мозжечка.

Две области моторного терминала уровня психики:
Область Брока VerbalFromIdArr=make(map[int]*Verbal)
отвечает за смысл распознанных слов и словосочетаний,
за конструирование собственных словосочетаний,
за моторное использование сло и словосочетаний.
За все ответственная структура - образ осмысленных слов и сочетаний.

Область моторных действий ActivityFromIdArr=make(map[int]*Activity)
отвечает за смысл распознанных действий с Пульта,
за конструирование собственных последовательностей действий,
за моторное использование действий.
За все ответственная структура - образ осмысленных действий и их сочетаний.

*/

package psychic

import (
	"BOT/brain/gomeostas"
	_ "BOT/brain/gomeostas"
	termineteAction "BOT/brain/terminete_action"
	"BOT/brain/transfer"
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
)

// блокировка действий во сне и при совершаемых действиях
var MotorTerminalBlocking = false

// ссылка на запущенный автоматизм перекрывается следующим запуском, т.е. всегда есть инфа о последнем запущенном
var lastAutomatizmRun *Automatizm
var lastAutomatizmRunPulsCount = 0

/*
НАЧАЛО ПЕРИОДА ОЖИДАНИЯ ОТВЕТА с Пульта
момент запуска автоматизма в числе пульсов -
только если LastAutomatizmWeiting был в ответ на действия Оператора!
Cбрасывать ожидание результата автоматизма если прошло WaitingPeriodForActionsVal (60) пульсов
*/
var LastRunAutomatizmPulsCount = 0 //
// период ожидания реакции оператора на действие автоматизма
const WaitingPeriodForActionsVal = 60

// ожидается результат запущенного MotAutomatizm
var LastAutomatizmWeiting *Automatizm

// предыдущий запущенный MotAutomatizm
var prevLastAutomatizmWeiting *Automatizm

// активный узел дерева в момент запуска автоматизма
var LastDetectedActiveLastNodID = 0

/*
	предыдущий момент запуска автоматизма

теперь prevLastDetectedActiveLastPulsCount==lastAutomatizmRunPulsCount НО НЕ СБРАСЫВАЕТСЯ, а есть всегда
*/
var prevLastDetectedActiveLastPulsCount = 0

/*
	запуск автоматизма на выполнение

возвращает true при успехе
*/
func RumAutomatizmID(id int) bool {

	//	a:= AutomatizmFromId[id]
	a, ok := ReadeAutomatizmFromId(id)
	if !ok {
		return false
	}
	return RumAutomatizm(a)
}

/*
	запрос из рефлексов, можно ли выполнять рефлекс if !psychic.getAllowReflexRuning(){ return }

РЕфлексы разблокируются через
*/
var notAllowReflexRuning = false

func GetAllowReflexRuning() bool {
	if notAllowReflexRuning || MotorTerminalBlocking {
		return false
	}
	return true
}

/*
	Запуск автоматизма с передачей строки на Пульт c

"10|"+message

	isTeachQuestion=true - Показать непонимание, растерянность с предложение научить

в случае отсуствия пси-реакций с вопросом о том, как нужно реагировать.

При запуске вторичного автоматизма Next не устанавливаются lastAutomatizmRun и т.п. переменные,
т.о. в периоде ожидания Эффект реакции применяется только к первому, пусковому автоматизму цепочки.
*/
var isTeachQuestion = false
var isInterruptAutmtzm = false

func RumAutomatizm(am *Automatizm) bool {
	//var isActiveLastRunAutomatizmPulsCount bool // маркер срабатывания фиксации активность мот. автоматизма
	isInterruptAutmtzm = false

	if am == nil {
		isInterruptAutmtzm = true
		return false
	}
	if MotorTerminalBlocking { //блокировка моторных терминалов во сне или произвольно
		isInterruptAutmtzm = true
		return false
	}

	//if wasRunTreeStandardAutomatizm { // уже был запущен штатный автоматизм после Стимула. ТЕПЕРЬ всегда после запуска автоматизма LastRunAutomatizmPulsCount >0
	if LastRunAutomatizmPulsCount > 0 {
		isInterruptAutmtzm = true
		return false
	}
	if wasRunPurposeActionFunc { // если ранее был запущен ментально в infoFunc17
		isInterruptAutmtzm = true
		return false
	}

	// NotAllowAnyActions ставится тогда, когда сохранение памяти должно выполняться в тишине, в бездействии
	if NotAllowAnyActions {
		isInterruptAutmtzm = true
		return false
	}
	if am.ActionsImageID == 0 {
		isInterruptAutmtzm = true
		return false
	}

	// блокировка выполнения плохого автоматизма, если только не применена "СИЛА ВОЛИ"
	if am.Usefulness < 0 && am.BranchID > 0 {
		isInterruptAutmtzm = true
		lib.WritePultConsol("Блокировка выполнения плохого (Usefulness=" + strconv.Itoa(am.Usefulness) + ") автоматизма iD=" + strconv.Itoa(am.ID))
		return false
	}

	// описание автоматизма
	res := GetAutomotizmActionsString(am, true, false) // здесь пишется "Энергичность"
	if len(res) == 0 {                                 // может не быть акции, а фраза не распознана, тогда ничего нет
		// такой автоматизм нужно удалить
		deleteAutomatizm(am)
		isInterruptAutmtzm = true
		return false
	}

	//	if am.BranchID > 0 {  любоый запуск автоматизма, даже пробный, без привязки к узлу
	notAllowReflexRuning = true // блокировка рефлексов
	// для учительского правила
	if LastAutomatizmWeiting == nil {
		prevLastAutomatizmWeiting = am
	} else {
		prevLastAutomatizmWeiting = LastAutomatizmWeiting
	}
	LastAutomatizmWeiting = am
	lastAutomatizmRun = am
	/* 	if lastAutomatizmRunPulsCount == 0 {
		prevLastDetectedActiveLastPulsCount = PulsCount
	} else {
		prevLastDetectedActiveLastPulsCount = lastAutomatizmRunPulsCount
	}		 */
	// всегда показывать время запуска автоматизма, в принципе теперь prevLastDetectedActiveLastPulsCount==lastAutomatizmRunPulsCount
	prevLastDetectedActiveLastPulsCount = PulsCount

	lastAutomatizmRunPulsCount = PulsCount
	//	}

	if NoautomatizmAfterStimul > 0 { // чтобы ставить прерывание
		// ПО-ЛЮБОМУ УБИРАЕМ МЕТКУ - ДЕТЕКТОР
		NoautomatizmAfterStimul = 0 // автоматизм выполнен, обнуляем метку
	}

	stimulCount = 0 //сколько раз был стимул от оператора после последнего запуска Ответа

	var out = "3|" // Бессознательный Автоматизм

	ta := ""
	switch levelOfRunAutomatizm {
	case 0:
		ta = "Штатный"
	case 1:
		ta = "1 уровень"
	case 2:
		ta = "2 уровень"
	case 3:
		ta = "Мышление"
	}
	out += "<div style=\"position:absolute;top:-10px;right:0;color:gray;\">" + ta + "</div>"
	//out += "<span style=\"position:relative;top:-10px;\">WWWWWWWW</span>"
	levelOfRunAutomatizm = 0

	// записать в инфо-окружение - память о происходящем сохраняется в массиве InformationEnvironmentObjects []*InformationEnvironment
	if am.BranchID > 0 {
		CurrentInformationEnvironment.AnswerImageID = lastAutomatizmRun.ActionsImageID

		//if isActiveLastRunAutomatizmPulsCount == true {
		if len(res) < 2 { // пустой и без запуска LastRunAutomatizmPulsCount не пускать
			return false
		}
		if isTeachQuestion {
			out = "10|Ответь сам на &quot;<b>" + res + "</b>&quot; чтобы показать, как лучше ответить."
			curActiveActionsPulsCount = 0
		} else {
			out += res
		}
		/*		} else {
				// не сработало isActiveLastRunAutomatizmPulsCount в процессе выполнения func13
				if isTeachQuestion {
					passivationAutomatizm(am, -1)
					return false
				}
				out += res
			}*/
	} else {
		out += res
	}

	/* на стадиях меньше 4 не активируем флаг потому, что он так и будет висеть в true и все блокировать. False ставится в consciousnessElementary(), которая активна только с 4 стадии
	if EvolushnStage > 3 {
		wasRunTreeStandardAutomatizm = true //Был запущен штатный автоматизм
	ТЕПЕРЬ всегда после запуска автоматизма LastRunAutomatizmPulsCount >0
	}*/

	lib.SentActionsForPult(out)
	isTeachQuestion = false //надо скидывать при любом раскладе - прошел попугайский автоматизм или нет

	// отслеживать последствия в automatizm_result.go при любом срабатывании автоматизма
	/* (было: начать ПЕРИОД ОЖИДАНИЯ реакции оператора - только при Стимуле Оператора, а не изменением состояния)
	Период ожидания должен начинаться ЗАНОВО после каждого ответа Beast и независимо от того, привязан ли автоматизм к ветке.
	*/
	//	if ActivationTypeSensor > 1 && am.BranchID > 0 {
	/*От Стимула curActiveActionsPulsCount до Ответа PulsCount должно быть было не более 3-х пульсов
	  		(потому как бот не может думать так долго
	  		!!!!С ЦИКЛАМИ МОЖЕТ ВЫДАТЬ ПРОИЗВОЛЬНЫЙ ОТВЕТ типа AmtzmNextString хоть через сколько времени!!!!),
	  		чтобы начался период ожидания,
	  		иначе ответ был явно не на Стимул, а, м.б. - по инициативе Beast (по ответу без стимула Правило не пишется).
	  Даже если возник в ходе решения доминанты,то он сразу не выдается на Пульт, а записывается в правило - уже более высокого порядка.
	*/
	// свежесть Стимула оператора - не позже, чем limitOfActionsAfterStimul пульса до Ответа на него
	//	if curActiveActionsID > 0 && (curActiveActionsPulsCount > (PulsCount - limitOfActionsAfterStimul)) {
	LastRunAutomatizmPulsCount = PulsCount // активность мот.автоматизма в чисде пульсов
	detectedActiveLastNodPrevID = detectedActiveLastNodID
	detectedActiveLastUnderstandingNodPrevID = detectedActiveLastUnderstandingNodID
	// может быть больше 3 пульсов - с этим надо что то делать
	// причина - циклы запускающие функции
	//isActiveLastRunAutomatizmPulsCount = true

	setAutomatizmRunning(am, curPurposeGeneticAutmtzm)
	//	}
	//	}

	/* при отзеркаливании на 2 и 4 стадиях нельзя делать попугайские с Usefulnes==-1 - иначе они могут разблокироваться и стать штатными в checkForUnbolokingAutomatizm()!!!
	кроме того, если заминусовать Usefulnes и при этом не отработал вопрос-ответ типа [out="10|Ответь сам на...] то получится заминусованный попугайский автоматизм
	который потом не даст ничего привязать к узлу, придется меняя пусковой стимул привязывать автоматизм к другому
	при создании автоматизма Usefulness и Belief ==0
	*/
	/*		if isTeachQuestion{
			isTeachQuestion = false
			am.Usefulness=0
			SetAutomatizmBelief(am,0)
		}*/
	if am.BranchID > 0 {
		//выполнить мозжечковый рефлекс сразу после выполняющегося автоматизма
		if !transfer.IsPsychicGameMode { // в игровом режиме нельзя корректировать действия - здесь только запись нового
			runCerebellumAdditionalAutomatizm(0, am.ID)
		}
		LastDetectedActiveLastNodID = detectedActiveLastNodID
		/* Блокировать выполнение рефлексов на время ожидания результата автоматизма
		вызывается из reflex_action.go рефлексов
		*/
		//isReflexesActionBloking=true // отмена в automatizm_result.go или просто isReflexesActionBloking=false

		return true
	}
	return false
}

func GetAutomotizmActionsString(am *Automatizm, writeLog bool, infoPult bool) string {
	var out = ""

	//ai:=ActionsImageArr[am.ActionsImageID]
	ai, ok := ReadeActionsImageArr(am.ActionsImageID)
	if !ok {
		lib.WritePultConsol("Нет карты ActionsImageArr для образа действий iD=" + strconv.Itoa(am.ActionsImageID))
		return ""
	}

	actAtr := ""
	praseStr := ""

	if ai.ActID != nil && am.BranchID > 0 {
		// учесть рефлекс мозжечка
		var addE int
		// для инфо-окна по щелчку таблицы автоматизмов на пульте не нужно накручивать энергию - надо просто показать текущую
		if !infoPult {
			addE = getCerebellumReflexAddEnergy(0, am.ID)
		}
		sumEnergy := am.Energy + addE
		if sumEnergy > 10 {
			sumEnergy = 10
		}
		if sumEnergy < 1 {
			sumEnergy = 1
		}
		//am.Count++
		actAtr = TerminateMotorAutomatizmActions(am.ID, ai.ActID, sumEnergy, infoPult)
	}

	if ai.PhraseID != nil && am.BranchID > 0 {
		addE := getCerebellumReflexAddEnergy(0, am.ID)
		praseStr = TerminatePraseAutomatizmActions(ai.PhraseID, am.Energy+addE)
	}
	// может не быть акции, а фраза не распознана, тогда ничего нет
	if len(actAtr) == 0 && len(praseStr) == 0 {
		return ""
	}
	out += actAtr + praseStr

	if ai.ToneID != 0 {
		out += "<br>" + getToneStrFromID(ai.ToneID) + "<br>"
	}

	if ai.MoodID != 0 {
		out += "<br>" + getMoodStrFromID(ai.MoodID) + "<br>"
	}
	if writeLog && am.BranchID > 0 {

		lib.WritePultConsol("<span style='color:blue;background-color:#FFFFA3;'>Запускается АВТОМАТИЗМ ID=" + strconv.Itoa(am.ID) + " с действием ID = " + strconv.Itoa(am.ActionsImageID) + " " + out + "</span>: ")

	}
	if am.NextID > 0 {
		out += showNextAtmtzmAction(am.ID, am.NextID, am.Energy)
	}

	// сбросить метку Не было действий более 100 пульсов
	CurrentInformationEnvironment.IsIdleness100pulse = false

	return out
}

// для функций пульта
func GetAutomotizmIDString(id int) string {

	//	am:= AutomatizmFromId[id]
	am, ok := ReadeAutomatizmFromId(id)
	if !ok {
		return "Нет автоматизма с ID = " + strconv.Itoa(id)
	}
	var out = ""
	//	ai:=ActionsImageArr[am.ActionsImageID]
	ai, ok := ReadeActionsImageArr(am.ActionsImageID)
	if ok {
		// учесть рефлекс мозжечка
		addE := getCerebellumReflexAddEnergy(0, am.ID)
		sumEnergy := am.Energy + addE
		if sumEnergy > 10 {
			sumEnergy = 10
		}
		if sumEnergy < 1 {
			sumEnergy = 1
		}
		//am.Count++
		out += TerminateMotorAutomatizmActions(am.ID, ai.ActID, sumEnergy, true)
	}

	if ai.PhraseID != nil {
		addE := getCerebellumReflexAddEnergy(0, am.ID)
		out += TerminatePraseAutomatizmActions(ai.PhraseID, am.Energy+addE)
	}
	if len(out) == 0 {
		return "ПУСТО..."
	}

	if ai.ToneID != 0 {
		out += "<br>" + getToneStrFromID(ai.ToneID) + "<br>"
	}

	if ai.MoodID != 0 {
		out += "<br>" + getMoodStrFromID(ai.MoodID) + "<br>"
	}

	return out
}

/*
	совершить МОТОРНОЕ (http://go/pages/terminal_actions.php) действие  - Dnn-часть автоматизма (не фраза)

cила действия сначала задается =5, а потот корректируется мозжечковыми рефлексами
Использование: 	TerminateMotorAutomatizmActions(actIDarr,energy)
*/
var rumAutomatizmOldID = 0 //
var rumAutomatizmOldEnergi = 0

func TerminateMotorAutomatizmActions(amID int, actIDarr []int, energy int, infoPult bool) string {
	// energy=1
	var out = ""
	var isAct = false
	for i := 0; i < len(actIDarr); i++ {
		if len(out) > 0 {
			out += ", "
		}
		// при моторном действии  меняются гомео-параметры:
		expensesGomeostatParametersAfterAction(actIDarr[i], energy)
		// выдать на Пульт:
		actName := termineteAction.TerminalActonsNameFromID[actIDarr[i]]
		// ЭНЕРГИЧНОСТЬ
		switch energy {
		case 1:
			out += "<span style=\"font-size:10px;\">" + actName + "</span>"
		case 2:
			out += "<span style=\"font-size:11px;\">" + actName + "</span>"
		case 3:
			out += "<span style=\"font-size:12px;\">" + actName + "</span>"
		case 4:
			out += "<span style=\"font-size:13px;\">" + actName + "</span>"
		case 5:
			out += "<span style=\"font-size:14px;\">" + actName + "</span>"
		case 6:
			out += "<span style=\"font-size:14px;\"><b>" + actName + "<b></span>"
		case 7:
			out += "<span style=\"font-size:17px;color:#927ACC\"><b>" + actName + "<b></span>"
		case 8:
			out += "<span style=\"font-size:19px;color:#E8A7A7\"><b>" + actName + "<b></span>"
		case 9:
			out += "<span style=\"font-size:21px;color:#E86966\"><b>" + actName + "<b></span>"
		case 10:
			out += "<span style=\"font-size:25px;color:#FF0000\"><b>" + actName + "<b></span>"
		}
		isAct = true
	}
	if isAct {
		/*Если втоматизм повторяется при одном и том же стимуле,
		то чисто "рефлекторно" повышать его силу действия с каждым разом, без мозжечкового механизма
		чтобы потом в одиночном вызове он не срабатывал.
		*/
		if rumAutomatizmOldID == amID { // повторился автоматизм
			// в игровом режиме это не учитываем, там постоянно используются одни и теже учительские кнопки при обучении
			// так же при показе инфо-окон по щелчку по строке таблицы автоматизмов на пульте просто показываем текущий уровень энергии
			if !transfer.IsPsychicGameMode && !infoPult {
				if energy+rumAutomatizmOldEnergi < len(termineteAction.EnergyDescrib) { // не превышать максимум
					rumAutomatizmOldEnergi++
				}
				energy += rumAutomatizmOldEnergi
			}
		} else {
			rumAutomatizmOldEnergi = 0
		}
		rumAutomatizmOldID = amID

		// название силы:
		var enegrName = ""
		if energy < len(termineteAction.EnergyDescrib) { // не превышать максимум
			enegrName = termineteAction.EnergyDescrib[energy]
		}
		font := getFontFromEnergy(energy)
		out = "Действие: <b>" + out + "</b><br><span style=\"" + font + "\">Энергичность: <b>" + enegrName + "</b></span><br>"

		return out
	}
	return ""
}

func getFontFromEnergy(energy int) string {
	switch energy {
	case 0:
		return "font-size:10px;color:#888888"
	case 1:
		return "font-size:11px;color:#666666"
	case 2:
		return "font-size:12px;color:#6699FF"
	case 3:
		return "font-size:13px;color:#0033FF"
	case 4:
		return "font-size:14px;color:#660099"
	case 5:
		return "font-size:15px;color:#000000"
	case 6:
		return "font-size:17px;color:#663300"
	case 7:
		return "font-size:19px;color:#CC6633;text-shadow: 0 0 1px #F74447,0 0 2px #F7B4C2,0 0 3px #F7DAE1;"
	case 8:
		return "font-size:22px;color:#FF3300;letter-spacing: 1px;text-shadow: 0 0 2px #F74447,0 0 4px #F7B4C2,0 0 8px #F7DAE1;"
	case 9:
		return "font-size:22px;color:#FF0066;letter-spacing: 2px;font-weight:bold;text-shadow: 0 0 2px #F74447,0 0 4px #F7B4C2,0 0 8px #F7DAE1;"
	case 10:
		return "font-size:22px;color:#000000"
	}
	return ""
}

/*
	совершить МОТОРНОЕ (ВЫДАТЬ ФРАЗУ) действие - Snn-часть автоматизма

cила действия сначала задается = 5, а потот корректируется мозжечковыми рефлексами
*/
func TerminatePraseAutomatizmActions(IDarr []int, energy int) string {
	// при моторном действии  меняются гомео-параметры:
	// expensesGomeostatParametersAfterAction(aI) болтать можно без устали?

	// выдать на ПУльт
	var out = ""
	if !isTeachQuestion {
		out += "Фраза Beast: "
	}

	for i := 0; i < len(IDarr); i++ {
		prase := word_sensor.GetPhraseStringsFromPhraseID(IDarr[i])
		if len(prase) > 0 {
			out += "<b>" + prase + "</b>"
		}
	}
	// название силы:
	if !isTeachQuestion {
		if energy < len(termineteAction.EnergyDescrib) {
			out += " " + termineteAction.EnergyDescrib[energy] + "</b>"
		}
	}
	return out
}

/*
	изменение гомео-параметров при действии

сила действия корректирует воздействие на параметр гомеостаза
*/
func expensesGomeostatParametersAfterAction(actID int, energy int) {
	if transfer.IsPsychicGameMode { // не воздействовать на гомео-параметры в игровом режиме
		return
	}
	se := termineteAction.TerminalActionsExpensesFromID[actID]
	if se != nil {
		for j := 0; j < len(se); j++ {
			// (2*aI.Energy/10) при силе==5 коэффициент будет 1, при силе==10 воздействие увеличиться в 2 раза
			if !gomeostas.NotAllowSetGomeostazParams {
				k := float64(2 * energy / 10)
				gomeostas.GomeostazParams[se[j].GomeoID] += se[j].Diff * k
				if gomeostas.GomeostazParams[se[j].GomeoID] > 100 {
					gomeostas.GomeostazParams[se[j].GomeoID] = 100
				}
				if gomeostas.GomeostazParams[se[j].GomeoID] < 0 {
					gomeostas.GomeostazParams[se[j].GomeoID] = 0
				}
			}
		}
	}
}

// запуск найденного автоматизма в цикле func consciousnessElementary()
func runConsciousnessAutomatizm(autmtzm *Automatizm) {
	if autmtzm == nil {
		return
	}
	RumAutomatizmID(autmtzm.ID)
	mentalInfoStruct.motorAtmzmID = 0
	mentalInfoStruct.noStaffAtmzmID = false
	motorActionEffect = autmtzm.Usefulness
}

/////////////////////////////////////////////////////////

/*
	запустить действие Игнорировать во время размышления if isRepressionStimulsNoise {

Это не автоматизм, а именно игнорирование - сообщение на пульт в виде действия
*/
func runIgnoreAction() {
	// игнорирования
	ignorImageID, _ := CreateNewlastActionsImageID(0, 0, []int{9}, nil, 0, 0, true)
	TerminateMotorAutomatizmActions(0, []int{ignorImageID}, 1, false)

	lib.WritePultConsol("<span style='color:blue;background-color:#FFFFA3;'>Игнорирование из-за глубокого размышления.</span>: ")

	// сбросить метку Не было действий более 100 пульсов
	CurrentInformationEnvironment.IsIdleness100pulse = false
}

//////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatizm_corrections.go
____________________________________________
/*  Корректировка автоматизма по результатам реакции
Блокирование, разблокирование автоматизмов

Блокирование - atmtzm.Usefulness<0 (такой не выполняется) или убирание из штатных.

Принципы.
1. изменение параметра atmtzm.Count в зависимоти от стадии развития: func EvolushnStageAtmzCount
В итоге, чтобы заблокировать автоматизм в 4 стадии, если он был закреплен во 2 стадии, нужно было несколько раз оценить его негативно.

2. в 4 версии надо видимо добавить новшество: изменять оценку автоматизма в зависимости от достижения/не достижения цели getMentalEffect,
причем с учетом значимости цели.
То есть здесь начинает влиять произвольная оценка значимости цели: чем она важнее, тем сильнее будет переоценка автоматизма.

3. блокировка должна быть ОЧЕНЬ КОНСЕРВАТИВНОЙ! потому как после негатива может возникнуть позитив (цепочка правил с конечным позитивом).
Животное очень не просто заставить что-то не делать.
Только очень сильный негатив (lastCommonDiffValue < -5) может блокировать автоматизм.

4. Разблокировка и переход в штатные привяхонного к ветке возможны только на уровне сознания,
если нет автоматизмов, а опыт позитива есть или ситуация очень скверная и нужно хоть что-то делать.
Для атасной ситуации применяется функция findAlntrnativeAtmtzm()
*/

package psychic

//////////////////////////////////////////////////////////////////

/*
зависимость шага надежности от стадии развития
чем ниже стадия - тем выше шаг. Это позволит закрепить от изменений
базовые автоматизмы, формируемые в начальных стадиях
*/
func EvolushnStageAtmzCount(stepType bool) int {
	var step = 1

	switch EvolushnStage {
	case 2:
		step = 5
	case 3:
		step = 2
	}
	if !stepType && step != 1 {
		step = step / 2
	}
	return step
}

////////////////////////////////////////////////////////////////////

/*
корректируется успешность автоматизма
lastCommonDiffValue - реакция на результат (эффект от -10 до +10)
wellIDarr - ID улучшившихся гомеопараметров

Здесь могут быть только штатные и с atmtzm.UsefulnessЮ=0, незаблокированные автоматизмы раз было совершено действие.
*/
func automatizmCorrection(atmtzm *Automatizm, lastCommonDiffValue int, wellIDarr []int) {
	if atmtzm == nil {
		return
	}

	var isCommon = false

	if atmtzm.BranchID > 1000000 { // это общий, привязанный не к ветке, а к действиям или словам
		isCommon = true
	}
	//коррекция эффекта в зависимости от достижения/не достижения цели
	if EvolushnStage > 3 {
		effectValuation := getMentalEffect(lastCommonDiffValue) // детектор достижения Цели
		wMentEffrt := 2                                         // вес эффекта достижения цели
		lastCommonDiffValue += wMentEffrt * effectValuation
		if lastCommonDiffValue != 0 { // иначе выдаст -10
			if lastCommonDiffValue > 10 {
				lastCommonDiffValue = 10
			}
			if lastCommonDiffValue < -10 {
				lastCommonDiffValue = -10
			}
		}
	}

	////////////// стало хуже
	if lastCommonDiffValue < 0 {
		PsyBaseMood = -1

		// это - игнорирующий автоматизм для предовтащения действия общего автоматизма в данной ветке?
		isIgnore := isIgnoreAutomatizm(atmtzm)
		if !isCommon && !isIgnore { // не для общего автоматизма и не для игнорирующего

			passivationAutomatizm(atmtzm, lastCommonDiffValue)

			if atmtzm.Usefulness < 0 {
				// очистить списки улучшения
				atmtzm.GomeoIdSuccesArr = nil
				if AutomatizmSuccessFromIdArr[atmtzm.ID] != nil {
					AutomatizmSuccessFromIdArr[atmtzm.ID] = nil
				}

				// убрать из штатного
				SetAutomatizmBelief(atmtzm, 0)
				if EvolushnStage > 3 {
					//в коллекцию неудач
					addNewTryAction(0, -detectedActiveLastProblemNodID, atmtzm.ActionsImageID, atmtzm.Usefulness, true)
					if atmtzm.NextID > 0 {
						// действия с цепочкой при плохом эффекте
						badEffectChainAtmtzm(atmtzm)
					}
				}
			}

		} else { // для общего автоматизма
			/*ОБЩИЕ АВТОМАТИЗМЫ НЕ КОРРЕКТИРУЮТСЯ негативом!
			Концепция общих автоматизмов. Они сформированы на основе общего шаблона рефлексов и, как и рефлексы,
			перекрываются автоматизмами конца активной ветки, т.е. имеющими образ Стимула.
			Это – первичная реакция на текущее сочетание контекстов (в данном случае – на эмоцию).
			У узла эмоции может быть бесконечное число образов действия
			и блокировка общего автоматизма лишает первичной реакции у всех их.
			Но у.рефлекс может быть заморожен, а общий автоматизм сейчас – нет.
			Это значит, то в случае блокирующих действий для данной ветки, необходимо запускать автоматизм бездействия,
			останавливающий все более низкоуровневое.
			При блокировке такого автоматизма опять НЕ БЛОКРУЕТСЯ.
			Наличие игнор.автоматизма в конце ветки для func consciousnessElementary() равноценно отсуствю автоматизма.
			*/
			getIgnoreAtmtzmToBrench(atmtzm.BranchID)
		}
	}
	//////////// стало лучше
	if lastCommonDiffValue > 0 {
		PsyBaseMood = 1
		// список гомео параметро, которые улучшило это действие
		if wellIDarr != nil {
			atmtzm.GomeoIdSuccesArr = wellIDarr // м.б. nil !!!! если нет таких явных действий
		}
		// пополняется список полезных автоматизмов
		AutomatizmSuccessFromIdArr[atmtzm.ID] = atmtzm

		// задать тип автоматизма, 2 - проверенный
		SetAutomatizmBelief(atmtzm, 2) // сделать автоматизм штатным

		if !isCommon { // не для общего автоматизма
			// повысить ранее плохой штатный автоматизм
			if atmtzm.Usefulness < lastCommonDiffValue {
				atmtzm.Usefulness = lastCommonDiffValue // позитивная надежность
			}
			atmtzm.Count += EvolushnStageAtmzCount(true) // добавляем счетчик позитивной надежности
		} else { // для общего автоматизма
			// привязать общий автоматизм к активной ветке
			linkCoomonAtmtzmToBrench(atmtzm)
		}
	}
	return
}

////////////////////////////

// ухудшение автоматизма негативом
func passivationAutomatizm(atmtzm *Automatizm, negativeValue int) {

	if atmtzm.Usefulness >= 0 {
		//зависимость шага надежности от стадии развития
		atmtzm.Count -= EvolushnStageAtmzCount(false)

		// Только очень сильный негатив или очень слабый автоматизм могут блокировать сразу. Или во второй стадии
		if negativeValue < -5 || (atmtzm.Usefulness == 0 && atmtzm.Count == 0) || (EvolushnStage == 2 && atmtzm.Count < 0) {
			atmtzm.Count = 1 // счетчик уверенности уже негативного автоматизма
			atmtzm.Usefulness = -1
		} else { // не очень сильный эффект
			// консервативное ухудшение рейтинга автоматизма, чтобы не сразу блокировался
			if atmtzm.Usefulness > 0 { // не понижать меньше, чем до 0
				atmtzm.Usefulness -= 1 //lastCommonDiffValue
				atmtzm.Count--
			}
			if atmtzm.Count < 0 {
				atmtzm.Count = 0
			}
		}
	} else { // уже негативный автоматизм
		atmtzm.Usefulness--
		atmtzm.Count++ // счетчик уверенности уже негативного автоматизма
	}
}

//////////////////////////////////////////////////////////////////////////

/*
срочно, не думая, найти аварийный автоматизм, хоть что-то из привязанных к ветке или новый
привязать, сделать штатным, даже с небольшим отрицательным Usefulness
*/
func findAlternativeAtmtzm() int {

	// список всех автоматизмов для ID узла Дерева
	aArr := GetMotorsAutomatizmListFromTreeId(detectedActiveLastNodID)
	if aArr != nil {
		var usefulCount = -100 // полезность, выбрать наилучшую
		var autmtzmID = 0
		var autmtzm *Automatizm
		if aArr != nil {
			for i := 0; i < len(aArr); i++ {
				if aArr[i].Belief == 2 { // есть штатный, проверенный автоматизм
					return aArr[i].ID
				}
				if aArr[i].Usefulness > -5 && aArr[i].Usefulness*aArr[i].Count > usefulCount {
					usefulCount = aArr[i].Usefulness * aArr[i].Count
					autmtzmID = aArr[i].ID
					autmtzm = aArr[i]
				}
			}
		}
		if autmtzm != nil {
			// выправить Usefulness и вернуть в штатные
			if autmtzm.Usefulness < 0 {
				autmtzm.Usefulness = 0
				autmtzm.Count = 1
			}
			SetAutomatizmBelief(autmtzm, 2) // сделать автоматизм штатным
			return autmtzmID
		}
	}
	// не найден, пробуем все правила, включая учительские, что есть в ответ на Стимул curStimulImageID
	ePsrsIdArr = nil
	getIdArr(3, &EpisodicTree, curStimulImageID, 0)
	if ePsrsIdArr != nil {
		// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
		ruleFromEpisodeIdArr(ePsrsIdArr, 1)
		// Выбрать одно лучшее Правило
		_, rule := findBestRule(targetEpisodicStrIdArr)
		if rule.Effect > 0 {
			// создать автоматизм, если такого нет и привязать к ветке
			oldID, am := checkUnicumMotorsAutomatizm(detectedActiveLastNodID, rule.Action)
			if oldID > 0 { // уже есть такой на ветке, хотя мы же выше проверяли, но на всякий, не создавать новый
				return oldID
			}
			if am != nil {
				// нужно создать дубликат автоматизма, чтобы старый не отвязывался от своей ветки
				id, _ := createDuplicateAutomatizm(detectedActiveLastNodID, am)
				if id > 0 {
					return id
				}
			}
		}
	}
	return 0
}

///////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatizm_functions.go
____________________________________________
/* функции подержки автоматизмов

Концепция общих автоматизмов. Они сформированы на основе общего шаблона рефлексов и, как и рефлексы,
перекрываются автоматизмами конца активной ветки, т.е. имеющими образ Стимула.
Это – первичная реакция на текущее сочетание контекстов (в данном случае – на эмоцию).
У узла эмоции может быть бесконечное число образов действия
и блокировка общего автоматизма лишает первичной реакции у всех их.
Но у.рефлекс может быть заморожен, а общий автоматизм сейчас – нет.
Это значит, то в случае блокирующих действий для данной ветки, необходимо запускать автоматизм бездействия,
останавливающий все более низкоуровневое.lib.MapCheckBlock(MapGwardAutomatizmFromId)
При блокировке такого автоматизма опять НЕ БЛОКРУЕТСЯ.
Наличие игнор.автоматизма в конце ветки для func consciousnessElementary() равноценно отсуствю автоматизма.
Функции:
// это - игнорирующий штатный автоматизм - расценивается как отсуствие реакции.
func isIgnoreAutomatizmID(atmtzmID int)bool{
func isIgnoreAutomatizm(atmtzm *Automatizm)bool{
// автоматизм игнорирования для остановки общего автоматизма, штатно привязанный к активной ветке detectedActiveLastNodID
func getIgnoreAtmtzmToBrench()(int,*Automatizm)
*/

package psychic

import (
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
	"sort"
)

//////////////////////////////////////////

/*
	Не раздумывая, а рефлексторно используя имеющуюся информацию,

ВЫБРАТЬ ЛУЧШИЙ АВТОМАТИЗМ для узла nodeID то более ранних, если нет у поздних.
а если нет, то учитывать общие автомтизмы, привязанные к действиям (виртуальная ветка ID от 1000000) и словам (>2000000)
*/
func getAutomatizmFromNodeID(nodeID int) int {
	if nodeID == 0 {
		return 0
	}

	//_, am := tryCreateAnswerForPhrese(2, detectedActiveLastNodID)
	//if am != nil {	}

	if isUnrecognizedPhraseFromAtmtzmTreeActivation { //при активации была нераспознанная фраза
		// это значит, что не может быть автоматизма при нераспознанной фразе и его нужно будет искать по infoFunc300
		return 0
	}

	// список всех автоматизмов для ID узла Дерева
	aArr := GetMotorsAutomatizmListFromTreeId(nodeID)
	var usefulness = -10 // полезность, выбрать наилучшую
	var autmtzm *Automatizm
	if aArr != nil {
		for i := 0; i < len(aArr); i++ {
			var allowRun = false

			if aArr[i].Usefulness < 0 {
				/* Не блокировать сразу, а посмотреть в Правила,
				м.б. после плохого эффекта последует следующее Правило с хорошим эффектом
				и тогда можно допустить Usefulness<0 в расчете на последующий успех.
				Не в качестве волевого усилия, а чисто автоматически использовать такую информацию?
				выбирая все хорошее (с эффектом, большим, чем  -aArr[i].Usefulness>0)
				Для сравнения используем положительное значение aArr[i].Usefulness
				*/

				//isWellEffect := isNextWellEffectFromActonRules(-aArr[i].Usefulness,aArr[i].ActionsImageID)
				// сравнивать эффект Правил с Usefulness автоматизма не корректно.... поэтому просто ищем для действия автоматизма.
				isWellEffect := isNextWellEffectFromActonRules(3, curActiveActionsID, aArr[i].ActionsImageID)
				if isWellEffect {
					allowRun = true // позволить выполниться автоматизму
				}
			}

			if aArr[i].Belief == 2 && allowRun { // есть штатный, проверенный автоматизм
				return aArr[i].ID
			}
			// сделаем приоритет выбора между одинаковыми usefulness за самым свежим
			// в 3 стадии это уберет косяк, когда создается новый вариант ответа на стимул, а на пульт отправляется старый
			// хотя новый автоматизм создается нормально
			// на стадиях больше 3 предпочтение отдаем штатному, который определяется при блокировке предыдущего штатного в SetAutomatizmBelief()/findBestNonStaff()
			// если не учитывать EvolushnStage > 3 то выбранный штатным в findBestNonStaff() автоматизм тут переназначится на "самый свежий", который не факт что лучший
			if aArr[i].Usefulness >= usefulness {
				if EvolushnStage > 3 && aArr[i].Usefulness >= 0 && aArr[i].Belief == 2 { // хотя Belief==2 и Usefulness >0 подразумевается по умолчанию, но подстрахуемся
					return aArr[i].ID
				} else {
					usefulness = aArr[i].Usefulness
					autmtzm = aArr[i]
				}
			}
		}
	}
	if usefulness >= 0 && autmtzm != nil { // выбран самый полезный из всех
		/*формирование не привязанных к узлу автоматизмов при активации дерева
		- для всех фраз - и для всех действий на основе привязанного автоматизма,
		чтобы другие ветки могли пользоваться при разных условиях.
		*/
		createNodeUnattachedAutomatizm(nodeID, autmtzm.ID)
		SetAutomatizmBelief(autmtzm, 2)
		return autmtzm.ID
	}
	//
	/* нет привязанных к данному узлу
	Это ситуация: фраза, для которой в узле нет автоматизма или нераспознанная фраза.
	*/
	if (curActions.PhraseID != nil && currentAutomatizmAfterTreeActivatedID == 0) || isUnrecognizedPhraseFromAtmtzmTreeActivation {
		/* Для текущей фразы сначала смотрим есть ли для данных условий (BaseID + EmotionID) автоматзмы для известных слов фразы.
		Если нет, то по Правилам выбираются подходяшие дейстивия.
		Если найдены действия, то создается автоматизм, который прикрепляется к узлу дерева автоматизмов branchID.
		В случае создания автоматизма возвращает autmtzm.ID иначе - 0.
		Если есть автоматизмы на "привет" и на "как дела?" то на "привет как дела?" должен сформироваться автоматизм из их действий.
		*/
		aID, atzm := tryCreateAnswerForPhrese(2, detectedActiveLastNodID) // 2 - максимальное число действия для автоматизма
		if aID > 0 && atzm.Usefulness >= 0 {                              // смотрим, чтобы не открыло заблокирванный автоматизм
			SetAutomatizmBelief(atzm, 2)
			return aID
		}
	}

	// на второй стадии и для функции 13 дальше не ищем, иначе начнет выкидывать общие автоматизмы
	if EvolushnStage == 2 || curFunc13ID > 0 {
		return 0
	}
	// в данном узле нет привязанного к нему автоматизма, либо автоматизм заблокирован
	// если это - узел действий или узел фразы, смотрим, если привязанные к таким объектам автоматизм

	//	node:=AutomatizmTreeFromID[nodeID] // должен быть обязательно, но...
	node, ok := ReadeAutomatizmTreeFromID(nodeID)
	if !ok {
		return 0
	}
	if node.PhraseID > 0 { // это узел фразы
		atmzS := GetAutomatizmBeliefFromPhraseId(node.PhraseID)
		if atmzS != nil {
			if autmtzm != nil {
				// если в найденном общем автоматизме такая акция, которая заблокирована в стандартном - не пускаем общий, ведь именно акцию и заблокировали
				if !isBranchUsefullnesAct(aArr, autmtzm.ActionsImageID) {
					return atmzS.ID //это - штатный автоматизм
				}
			} else {
				// если стандартных автоматизмов вообще нет, то запускаем общий
				return atmzS.ID //это - штатный автоматизм
			}
		}
	}
	/////////////
	if node.ActivityID > 0 && node.ToneMoodID == 90 { // это узел действий - конечный в активной ветке. 90 - ToneMoodID по умолчанию
		atmzA := GetAutomatizmBeliefFromActionId(node.ActivityID)
		if atmzA != nil {
			if autmtzm != nil {
				// если в найденном общем автоматизме такая акция, которая заблокирована в стандартном - не пускаем общий, ведь именно акцию и заблокировали
				if !isBranchUsefullnesAct(aArr, autmtzm.ActionsImageID) {
					return atmzA.ID //это - штатный автоматизм
				}
			} else {
				// для третьей стадии больше не ищем потому, что в случае со стимулом фраза + действие будет выдавать последний запущенный, а надо отзеркалить стимул
				if EvolushnStage == 3 {
					return 0
				}
				// если стандартных автоматизмов вообще нет, то запускаем общий
				return atmzA.ID //это - штатный автоматизм
			}
		}
	}

	//////////// нет штатных автоматизмов, выбрать любой нештатный на пробу
	/* такого быть не должно, т.к. штатный должен быть всегда
	if node.PhraseID>0 { // это узел фразы
		aArr = AutomatizmIdFromPhraseId[node.PhraseID]
		if aArr != nil {
			return aArr[0].ID // первый попавшийся не штатный, раз уже не нашелся штатный
		}
	}
	if node.ActivityID>0 && node.ToneMoodID==0 {
		aArr = AutomatizmIdFromActionId[node.PhraseID]
		if aArr != nil {
			return aArr[0].ID // первый попавшийся не штатный
		}
	}
	*/
	/////////// нет никаких автоматизмов хоть как-то относящихся к данному узлу
	// найти у предыдущих узел действий
	for i := len(ActiveBranchNodeArr) - 1; i > 2; i-- {

		//		node=AutomatizmTreeFromID[ActiveBranchNodeArr[i]]
		node, ok := ReadeAutomatizmTreeFromID(ActiveBranchNodeArr[i])
		if ok {
			atmzA := GetAutomatizmBeliefFromActionId(node.ActivityID)
			if atmzA != nil {
				if autmtzm != nil {
					// если в найденном общем автоматизме такая акция, которая заблокирована в стандартном - не пускаем общий, ведь именно акцию и заблокировали
					if !isBranchUsefullnesAct(aArr, autmtzm.ActionsImageID) {
						return atmzA.ID //это - штатный автоматизм
					}
				} else {
					// если стандартных автоматизмов вообще нет, то запускаем общий
					return atmzA.ID //это - штатный автоматизм
				}
				// не штатные автоматизмы для данного образа действий не будем смотреть
			}
		}
	}

	return 0
}

// проверка по действию и ветке дерева автоматизма - не заблокирован ли
func isBranchUsefullnesAct(aArr []*Automatizm, act int) bool {
	for _, v := range aArr {
		if v.ActionsImageID == act && v.Usefulness < 0 {
			return true
		}
	}
	return false
}

// выбрать самый успешный автоматизм ветки
func getBeastAutomatizmFromNodeID(node *AutomatizmNode) *Automatizm {
	if node == nil {
		return nil
	}
	// список всех автоматизмов для ID узла Дерева
	var aArr []*Automatizm
	for _, a := range AutomatizmFromId {
		if a == nil {
			continue
		}
		if a.BranchID == node.ID {
			aArr = append(aArr, a)
		}
	}

	var usefulness = -10 // полезность, выбрать наилучшую
	var autmtzm *Automatizm
	if aArr != nil {
		for i := 0; i < len(aArr); i++ {
			if aArr[i].Usefulness > usefulness {
				usefulness = aArr[i].Usefulness
				autmtzm = aArr[i]
			}
		}
		if usefulness >= 0 && autmtzm != nil {
			return autmtzm
		}
	}
	return nil
}

/////////////////////////////////////////////////
/*задать тип автоматизма Belief.
Только один из автоматизмов, прикрепленных к ветке или образу, может иметь Belief=2 - проверенное собственное знание
Если задается Belief=2, остальные Belief=2 становится Belief=0.
ТАК ПРОСТО НЕЛЬЗЯ ЗАДАВАТЬ Belief=2: LastAutomatizmWeiting.Belief=2

Для блокировки автоматизма нужно использовать SetAutomatizmBelief(atmzm,0) - для реализации func findBestNonStaff()
*/
func SetAutomatizmBelief(atmzm *Automatizm, belief int) {
	if atmzm == nil || atmzm.BranchID == 0 {
		return
	}
	if belief == 2 {
		atmzm.Belief = 2
		removeAllBeliefFormBranchID(atmzm) // все другие автоматизмы ветки вынести из штатных
		/* нельзя задавая Belief==2 оставлять Usefulness<0, он как минимум должен быть =0.
		 */
		if atmzm.Usefulness < 0 {
			atmzm.Usefulness = 0
			atmzm.Count = 1
		}
	}
	// при SetAutomatizmBelief(atmzm,0) - для выноса автоматизма из штатного
	if belief == 0 {
		atmzm.Belief = 0
		removeAllBeliefFormBranchID(atmzm) // все другие автоматизмы ветки вынести из штатных
		// посмотреть,  нет ли нештатные автоматизмы в ветке с Usefulness>0 т.е. заблокированных оператором под горячую руку
		aBest := findBestNonStaff(atmzm)
		if aBest != nil {
			aBest.Usefulness = 0 // разблокировать, сделав пробным штатным
			aBest.Belief = 2
		}
	}

	atmzm.Belief = belief
}

// //////////////////////////
// все автоматизмы ветки вынести из штатных
func removeAllBeliefFormBranchID(atmzm *Automatizm) {
	if atmzm == nil || atmzm.BranchID == 0 {
		return
	}
	// привязанные к ID узла дерева
	if atmzm.BranchID < 1000000 { // обнулить Belief у всех привязанных к узлу

		aArr := GetMotorsAutomatizmListFromTreeId(atmzm.BranchID)

		if len(aArr) > 1 {
			for i := 0; i < len(aArr); i++ {
				if aArr[i] != atmzm && aArr[i].Belief == 2 {
					aArr[i].Belief = 0
					AutomatizmBelief2FromTreeNodeId[aArr[i].BranchID] = nil
				}
			}
		}
		AutomatizmBelief2FromTreeNodeId[atmzm.BranchID] = atmzm
	}
	// привязанные к ID образа действий с пульта ActivityID
	if atmzm.BranchID > 1000000 && atmzm.BranchID < 2000000 { // обнулить Belief у всех привязанных к ActivityID
		imgID := atmzm.BranchID - 1000000
		for _, v := range AutomatizmIdFromActionId[imgID] {
			v.Belief = 0
		}
	}
	if atmzm.BranchID > 2000000 { // обнулить Belief у всех привязанных к PhraseID
		imgID := atmzm.BranchID - 2000000
		for _, v := range AutomatizmIdFromPhraseId[imgID] {
			v.Belief = 0
		}
	}
}

/*
найти нештатные автоматизмы в ветке с Usefulness>0 т.е. заблокированные оператором под горячую руку
кроме данного: atmzm *Automatizm
Только для нормальных (не групповых) автоматизмов.

Восстановление блокированных автомаптизмов ветки.
*/
func findBestNonStaff(atmzm *Automatizm) *Automatizm {
	if atmzm == nil || atmzm.BranchID == 0 {
		return nil
	}
	beat := 0
	var aBest *Automatizm

	aArr := GetMotorsAutomatizmListFromTreeId(atmzm.BranchID)

	if len(aArr) > 1 {
		for i := 0; i < len(aArr); i++ {
			if aArr[i].ID == atmzm.ID {
				continue
			}
			if aArr[i].Usefulness > 0 && aArr[i].Usefulness > beat {
				beat = aArr[i].Usefulness
				aBest = aArr[i]
			}
		}
	}
	if aBest != nil {
		return aBest
	}
	return nil
}

/////////////////////////////////////////////////////

// список всех автоматизмов для ID узла Дерева
var lastAutomatizmArrFromNodeID []*Automatizm // уже полученный массив для lastAutomatizmsNodeID чтобы не повторяться
var lastAutomatizmsNodeID int

func GetMotorsAutomatizmListFromTreeId(nodeID int) []*Automatizm {
	if nodeID == 0 {
		return nil
	}

	if AutomatizmFromId == nil {
		return nil
	}
	if lastAutomatizmsNodeID == nodeID { // уже есть массив для nodeID
		return lastAutomatizmArrFromNodeID
	}
	lastAutomatizmsNodeID = nodeID

	lastAutomatizmArrFromNodeID = nil

	for _, a := range AutomatizmFromId {
		if a == nil {
			continue
		}
		if a.BranchID < 1000000 && a.BranchID == nodeID {
			lastAutomatizmArrFromNodeID = append(lastAutomatizmArrFromNodeID, a)
		}
	}

	// нужно для работы в 3 стадии
	sort.SliceStable(lastAutomatizmArrFromNodeID, func(i, j int) bool {
		return lastAutomatizmArrFromNodeID[i].ID < lastAutomatizmArrFromNodeID[j].ID
	})
	return lastAutomatizmArrFromNodeID
}

// штатный, невредный автоматизм, привязанный к ветке
func GetBelief2AutomatizmListFromTreeId(nodeID int) *Automatizm {
	if nodeID == 0 {
		return nil
	}
	aArr := AutomatizmBelief2FromTreeNodeId[nodeID]

	if aArr == nil {
		return nil
	}
	if aArr.Usefulness >= 0 { // есть штатный, невредный
		return aArr
	}
	return nil
}

//////////////////////////////////////////////////

// есть ли штатный автоматизм (с Belief==2), привязанные к узлу дерева
func ExistsAutomatizmForThisNodeID(nodeID int) bool {
	aArr := AutomatizmBelief2FromTreeNodeId[nodeID]
	if aArr != nil {
		return true
	}
	return false
}

///////////////////////////////////////

/*
	если для прикрепленных к узлу дерева есть карта штатных AutomatizmBelief2FromTreeNodeId,

то для прикрепленных к образам нужны ФУНКЦИИ ПОЛУЧЕНИЯ ШТАТНОГО ДЛЯ ДАННОГО ОБРАЗА
*/
func GetAutomatizmBeliefFromActionId(activityID int) *Automatizm {
	if AutomatizmIdFromActionId[activityID] == nil {
		return nil
	}

	for _, v := range AutomatizmIdFromActionId[activityID] {
		if v == nil {
			continue
		}
		//ai,ok:=ActionsImageArr[v.ActionsImageID]
		ai, ok := ReadeActionsImageArr(v.ActionsImageID)
		if !ok {
			continue
		}
		if ai.ActID != nil && v.Belief == 2 {
			return v
		}
	}
	return nil
}

// /////////////////////////////////////////////////
func GetAutomatizmBeliefFromPhraseId(verbalID int) *Automatizm {
	if AutomatizmIdFromPhraseId[verbalID] == nil {
		return nil
	}
	for _, v := range AutomatizmIdFromPhraseId[verbalID] {
		if v.Belief == 2 {
			return v
		}
	}
	return nil
}

/*
формирование не привязанных к узлу автоматизмов при активации дерева
- для всех фраз - и для всех действий на основе привязанного автоматизма,
чтобы другие ветки могли пользоваться при разных условиях.
*/
func createNodeUnattachedAutomatizm(nodeID int, aID int) {

	//	node:=AutomatizmTreeFromID[nodeID] // должен быть обязательно, но...
	node, ok := ReadeAutomatizmTreeFromID(nodeID)
	if !ok {
		return
	}
	//autmzm0:= AutomatizmFromId[aID] // должен быть обязательно, но...
	autmzm0, ok := ReadeAutomatizmFromId(aID)
	if !ok {
		return
	}

	if node.PhraseID > 0 { // это узел фразы
		_, autmzm := CreateAtutomatizmNoSaveFile(2000000+node.PhraseID, autmzm0.ActionsImageID)
		if autmzm != nil && autmzm.Usefulness >= 0 { // не даем открывать заблокированные
			autmzm.Usefulness = 0          // пока предположительно
			SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
		}
	}
	/*	получается некий аналог у-рефлекса, когда на разные стимулы выдается один и тот же ответ
		в данном случае на полный стимул "вербальный + действие" и частичные "вербальный", "действие" создаются обшие автоматизмы с одним ответом autmzm0.ActionsImageID
	*/if node.ActivityID > 0 && node.ToneMoodID == 90 { // это узел действий - конечный в активной ветке. 90 - ToneMoodID по умолчанию
		_, autmzm := CreateAtutomatizmNoSaveFile(1000000+node.ActivityID, autmzm0.ActionsImageID)
		if autmzm != nil && autmzm.Usefulness >= 0 { // не даем открывать заблокированные
			autmzm.Usefulness = 0          // пока предположительно
			SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
		}
		// можно выделить из ответа только действие и создать общий автоматизм с таким ответом, но тогда получится просто клон рефлекса
		// что и так происходит в стадиях начиная со 2. Поэтому пока под сомнением - надо ли так делать, может правильнее вариант выше
		/*		if actImg, ok:=ActionsImageArr[autmzm0.ActionsImageID]; ok{
				actimgID,_:=CreateNewlastActionsImageID(0, 0, actImg.ActID, nil, 0, 0, true)
				_,autmzm:= CreateAtutomatizmNoSaveFile(1000000+node.ActivityID, actimgID)
				if autmzm!=nil{
					autmzm.Usefulness=0 // пока предположительно
					SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
				}
			}*/
	}
}

// разблоикровака автоматизма для http://go/pages/automatizm_table.php
func UnblockAutomatizmID(atmtzmID int) string {

	//	atmtzm:= AutomatizmFromId[atmtzmID]
	atmtzm, ok := ReadeAutomatizmFromId(atmtzmID)
	if !ok {
		return "0"
	}
	atmtzm.Usefulness = 1
	atmtzm.Count = 1
	return "1"
}

func UnblockingAllAtmtzms() {
	for _, v := range AutomatizmFromId {
		if v == nil {
			continue
		}
		if v.Usefulness < 0 {
			v.Usefulness = 0
			v.Count = 1
		}
	}

}

/////////////////////////////////////////////////////////////////////

// привязать общий автоматизм к активной ветке detectedActiveLastNodID
func linkCoomonAtmtzmToBrench(commonAutomatizm *Automatizm) {
	if LastAutomatizmWeiting.BranchID < 1000000 { // это НЕ общий - не должно такого быть
		return
	}
	atmtzm := GetBelief2AutomatizmListFromTreeId(detectedActiveLastNodID)
	if atmtzm != nil && atmtzm.ID == commonAutomatizm.ID {
		return
	}
	CreateAtutomatizmNoSaveFile(detectedActiveLastNodID, commonAutomatizm.ActionsImageID)
}

/////////////////////////////////////////////////////////////////////

// автоматизм игнорирования для остановки общиего автоматизма, штатно привязанный к активной ветке
func getIgnoreAtmtzmToBrench(BranchID int) (int, *Automatizm) {
	// игнорирующее действие:
	aID, _ := CreateNewlastActionsImageID(0, 0, []int{9}, nil, 0, 0, true)
	if aID <= 0 {
		return 0, nil
	}
	id, atmtzm := createNewAutomatizmID(0, BranchID, aID, true)

	if atmtzm != nil {
		return 0, nil
	}
	SetAutomatizmBelief(atmtzm, 2)

	return id, atmtzm
}

// определение, что это - игнорирующий ID автоматизма
func isIgnoreAutomatizmID(atmtzmID int) bool {
	//	atmtzm,ok:=AutomatizmFromId[atmtzmID]
	atmtzm, ok := ReadeAutomatizmFromId(atmtzmID)
	if !ok {
		return false
	}

	return isIgnoreAutomatizm(atmtzm)
}

// пределение, что это - игнорирующий автоматизм
func isIgnoreAutomatizm(atmtzm *Automatizm) bool {
	if atmtzm == nil {
		return false
	}
	iaID, _ := CreateNewlastActionsImageID(0, 0, []int{9}, nil, 0, 0, true)
	if atmtzm.ActionsImageID == iaID {
		return true
	}

	return false
}

/////////////////////////////////////////////////////////////////////

/*
есть ли автоматизм с действием curStimulImageID, и если у него atmtzm.Usefulness<0 - ПОСТЕПЕННО снять блокировку
потому как это - новое авторитарное подтвержение полезности.
Для текущей ветки дерева автоматизмов.
*/
func checkForUnbolokingAutomatizm(actID int) {
	for _, v := range AutomatizmFromId {
		if v == nil {
			continue
		}
		// накручиваем только Usefulness==-1, Usefulness==0 - это попугайские автоматизмы, их не трогаем!
		// при отзеркаливании на 2 и 4 стадиях нельзя делать попугайские с Usefulnes==-1 - иначе они тут же разблокируются и станут штатными
		if v.BranchID == detectedActiveLastNodID && v.ActionsImageID == actID && v.Usefulness < 0 {
			v.Usefulness = v.Usefulness + 1 // ++ не срабатывает
			if v.Usefulness == 0 {
				//v.Usefulness=0  пока предположительно
				SetAutomatizmBelief(v, 2) // сделать автоматизм штатным, полезность 1 установится там же автоматически
			}
		}
	}

}

///////////////////////////////////////////////////////

/*
	найти узел ветки данного уроня, начиная с конечного узла branchID

Первый уровень - BaseID, 6-й уровень - PhraseID
*/
func getNodeFromLevel(level int, branchID int) *AutomatizmNode {

	//ln:=AutomatizmTreeFromID[branchID]
	ln, ok := ReadeAutomatizmTreeFromID(branchID)
	if !ok {
		return nil
	}
	// сначала получить спиоск всех узлов ветки
	var nArr []*AutomatizmNode
	for ln != nil {
		nArr = append(nArr, ln)
		ln = ln.ParentNode
	}

	// берем второй уровень с эмоцией, удалив последние 2
	eNode := nArr[len(nArr)-3]
	return eNode
}

/////////////////////////////////////////////////////////////////

/*
	начать поиск с данного узла по словам поиском по дереву - лучший автоматизм из найденных

пройти 2 урояня: ActivityID и ToneMoodID до SimbolID
*/
func getAtmtzmFromNodesFrase(node *AutomatizmNode, phraseID int) *Automatizm {
	//FirstSimbolID:=word_sensor.GetFirstSymbolFromWordID(wordIDarr[0])
	FirstSimbolID := word_sensor.GetFirstSymbolFromPraseID([]int{phraseID})

	var atmzmArr []*Automatizm

	for k := 0; k < len(node.Children); k++ { // тут ActivityID
		nextlev1 := &node.Children[k]
		for l := 0; l < len(nextlev1.Children); l++ { // тут ToneMoodID
			nextlev2 := &nextlev1.Children[l]
			for m := 0; m < len(nextlev2.Children); m++ { // тут SimbolID
				if FirstSimbolID != nextlev2.Children[m].SimbolID {
					continue
				}
				nextlev3 := &nextlev2.Children[m]

				//if nextlev3.PhraseID == phraseID {
				if existsPraseIDinVerbID(nextlev3.PhraseID, phraseID) {
					a := getBeastAutomatizmFromNodeID(nextlev3)
					if a != nil {
						atmzmArr = append(atmzmArr, a)
					}
				}
				for n := 0; n < len(nextlev3.Children); n++ { // тут PhraseID
					nextlev4 := &nextlev3.Children[n]
					/*
						AutomatizmTreeMapCheck()
						verbal,ok:=VerbalFromIdArr[nextlev.PhraseID]
						if ok {
							wArr:=word_sensor.GetWordsIDarrFromPraseNodeID(verbal.PhraseID[0])
							if lib.EqualArrs(wArr, phraseArr){
								a:=getBeastAutomatizmFromNodeID(nextlev)
								atmzmArr=append(atmzmArr,a)
							}
						}*/
					//if nextlev4.PhraseID == phraseID {
					if existsPraseIDinVerbID(nextlev4.PhraseID, phraseID) { //// есть ли в данном Verb фраза praseID int
						a := getBeastAutomatizmFromNodeID(nextlev4)
						if a != nil {
							atmzmArr = append(atmzmArr, a)
						}
					}
				}
			}
		}
	}
	if atmzmArr != nil { // выбор лучшего
		max := -1
		var curA *Automatizm
		for n := 0; n < len(atmzmArr); n++ {
			if atmzmArr[n].Usefulness > max {
				max = atmzmArr[n].Usefulness
				curA = atmzmArr[n]
			}
		}
		if curA != nil {
			return curA
		}
	}

	return nil
}

///////////////////////////////////////////////////////////////////

/*
	Есть ли в узде дерева автоматизмов автоматизм с образом действий ActionsImage?

Когда для данного узла дерева автоматизмов находится новый образ действий,
нужно проверять, а не заблокирован ли такой ранее.
Если да, то незачем создавать такой же снова.

Если находит, то возвращает ID автоматизма.
*/
func existsThistActionIdInAtreeNode(nodeID int, ActionsImage int) (bool, int) {
	// это может быть вызов при создании нового автоматизма по учительскому правилу, поэтому nodeID - не обязательный параметр
	if nodeID == 0 {
		return false, 0
	}
	if ActionsImage == 0 {
		lib.TodoPanic("В func existsThistActionIdInAtreeNode неверный параметр!")
		return false, 0
	}
	// список всех автоматизмов для ID узла Дерева
	aArr := GetMotorsAutomatizmListFromTreeId(nodeID)
	if aArr == nil {
		return false, 0
	}
	for i := 0; i < len(aArr); i++ {
		if aArr[i].ActionsImageID == ActionsImage {
			return true, aArr[i].ActionsImageID
		}
	}

	return false, 0
}

____________________________________________

BOT/brain/psychic/automatizm_list_pult_info.go
____________________________________________
/* список моторных автоматизмов для Пульта
для http://go/pages/automatizm_table.php
*/

package psychic

import (
	"BOT/brain/gomeostas"
	termineteAction "BOT/brain/terminete_action"
	"BOT/lib"
	"sort"
	"strconv"
	"strings"
)

//////////////////////////////////////////

// //////////////////////////////////////////
func GetAutomatizmInfo(limitBasicID int) string {
	var out = ""
	// сколько рефлексов есть
	uAutomatizmCount := len(AutomatizmFromId)
	// переключатель диапазона вывода
	out += "<br>Показывать: "

	out += "<span style='cursor:pointer;"
	if limitBasicID == 0 { // показать 100 последних с обратной сортировкой
		out += "background-color:#FFFF9D;font-weight:bold;"
	}
	out += "' onClick='show_level(0)'>100последних</span> "

	out += "<span style='cursor:pointer;"
	if limitBasicID == 1 {
		out += "background-color:#FFFF9D;font-weight:bold;"
	}
	out += "' onClick='show_level(1)'>Плохо</span> "

	out += "<span style='cursor:pointer;"
	if limitBasicID == 2 {
		out += "background-color:#FFFF9D;font-weight:bold;"
	}
	out += "' onClick='show_level(2)'>Норма</span> "

	out += "<span style='cursor:pointer;"
	if limitBasicID == 3 {
		out += "background-color:#FFFF9D;font-weight:bold;"
	}
	out += "' onClick='show_level(3)'>Хорошо</span> "

	out += "<span style='cursor:pointer;"
	if limitBasicID == 4 {
		out += "background-color:#FFFF9D;font-weight:bold;"
	}
	out += "' onClick='show_level(4)' title='Автоматизмы, не привязанные к определенным условиям состояния Beast.'>Свободные</span> "

	out += "<span style='cursor:pointer;"
	if limitBasicID == 5 {
		out += "background-color:#FFFF9D;font-weight:bold;"
	}
	out += "' onClick='show_level(5)' title='Автоматизмы с плохой Полезностью, которые блокируются при запуске.'>Заблокированные</span> "
	/////////////////////////////////

	header := "<tr><th width=70 class='table_header'>ID ав-ма</th>" +
		"<th width=70 class='table_header' style='background-color:#CCC5FF;' title='Если <1000000, то - узел ветки дерева\nЕсли <2000000 - ID образа действий\nостальное - ID фразы'>ID <br>объекта<br>Привязки</th>" +
		"<th width=70 class='table_header' style='background-color:#CCC5FF;'  title='ID базового состояния'>BaseID Дерева</th>" +
		"<th width='70' class='table_header' style='background-color:#CCC5FF;'  title='ID образа сочетания эмоций'>Эмоции Дерева</th>" +
		"<th width='10%' class='table_header' style='background-color:#CCC5FF;'  title='ID образа пускового стимула'>ДействияID-ФразаID-НастроениеID</th>" +
		"<th class='table_header'  title='Действия автоматизма, их может быть много видов.'>Образ действия</th>" +
		"<th width='30' class='table_header' title='ID следующего автоматизма в цепочке'>NextID</th>" +
		"<th width='30' class='table_header' title='Энергичность'>Energy</th>" +
		"<th width='30' class='table_header' title='(БЕС)ПОЛЕЗНОСТЬ: -10 вред 0 +10 +n польза'>Польза</th>" +
		"<th width='30' class='table_header' title='Уверенность: 0 - пробник, 1 - чужие сведения, 2 - уверенный (штатный)'>Belief</th>" +
		"<th width='30' class='table_header' title='Число успешных использований'>Надежность</th></tr>"

	out += "<table class='main_table'  cellpadding=0 cellspacing=0 border=1 width='1000px' style='font-size:14px;'>" +
		header

	if len(AutomatizmFromId) == 0 {
		return out + "</table><br>Подождите пока не активируется психика (не более 4 секунд) и нажмите <a href='/pages/automatizm_table.php'>Обновить</a>"
	}

	keys := make([]int, 0, uAutomatizmCount)

	for k, v := range AutomatizmFromId {
		if v != nil {
			keys = append(keys, k)
		}
	}

	if limitBasicID == 0 { // обратная сортировка
		sort.Slice(keys, func(i, j int) bool {
			return keys[i] > keys[j]
		})
	} else {
		sort.Ints(keys)
	}
	///////////////////////////////////////////////////

	var count = 0
	for _, k := range keys {
		//v:= AutomatizmFromId[k]
		v, ok := ReadeAutomatizmFromId(k)
		if !ok {
			count++
			continue
		}
		if limitBasicID == 5 && v.Usefulness >= 0 {
			count++
			continue
		}
		if limitBasicID < 4 && v.BranchID >= 1000000 {
			count++
			continue
		}

		id := strconv.Itoa(k)
		nodeID := ""
		baseID := ""
		emotionID := ""
		emotionTitle := ""
		trStr := ""
		onclickID := ""
		//activityTitle:=""
		sStyle := "style='cursor:pointer;color:blue'"
		emClick := ""
		actClick := ""

		// узел дерева, к которому прикреплен автоматизм   && limitBasicID<4
		if v.BranchID < 1000000 {

			//	nodeA:=AutomatizmTreeFromID[v.BranchID]
			nodeA, ok := ReadeAutomatizmTreeFromID(v.BranchID)
			if ok {
				nodeID = strconv.Itoa(nodeA.ID)
				if limitBasicID > 0 && limitBasicID < 5 {
					if nodeA.BaseID != limitBasicID {
						count++
						continue
					}
				}
				baseID = strconv.Itoa(nodeA.BaseID) + " " + gomeostas.GetBaseCondFromID(nodeA.BaseID)

				/// эмоции
				emotionID = strconv.Itoa(nodeA.EmotionID)
				emo := EmotionFromIdArr[nodeA.EmotionID]
				if emo == nil { // так не должно быть! эмоция д.б. всегда
					//lib.WritePultConsol("В emo:=EmotionFromIdArr[nodeA.EmotionID] НЕТ ЭМОЦИИ!!!! Какой-то образ неверен, нужен программист!")
					// лучше бы выкинуть панику: panic("НЕТ ЭМОЦИИ!!!!")
					emotionTitle += "<span style='color:red'>НЕТ ЭМОЦИИ! нужно разобраться с EmotionFromIdArr[]</span>"
					count++
					continue
				}
				for i := 0; i < len(emo.BaseIDarr); i++ {
					if i > 0 {
						emotionTitle += ", "
					}
					emotionTitle += gomeostas.GetBaseContextCondFromID(emo.BaseIDarr[i])
				}
				/////////////////////////////
				if id == "19" {
					id = "19"
				}

				// пусковой стимул

				trStr = strconv.Itoa(nodeA.ActivityID) + "-" + strconv.Itoa(nodeA.PhraseID) + "-" + strconv.Itoa(nodeA.ToneMoodID)

				emClick = "onClick='show_emotion(" + emotionID + ")'"
				actClick = "onClick='show_trigger(" + nodeID + ")'"

			} //if nodeA!=nil{

			//if v.BranchID<1000000{
		} else {

			nodeID = strconv.Itoa(v.BranchID)
			onclickID = "onClick='show_object(" + strconv.Itoa(v.BranchID) + ")' style='cursor:pointer;color:blue'"
			baseID = "не"
			emotionID = "привязан"
			//	emotionTitle="привязан"
			trStr = "к дереву"
			sStyle = ""

		}

		//////////////
		out += "<tr >"
		out += "<td class='table_cell' >" + id + "</td>"
		out += "<td class='table_cell' " + onclickID + ">" + nodeID + "</td>"
		out += "<td class='table_cell' ><nobr>" + baseID + "</nobr></td>"
		out += "<td class='table_cell' title='" + emotionTitle + "' " + emClick + " " + sStyle + "><nobr>" + emotionID + "</nobr></td>"
		out += "<td class='table_cell' title='Информация по клику' " + actClick + " " + sStyle + ">" + trStr + "</td>"
		out += "<td class='table_cell' title='Информация по клику'  onClick='show_actions(" + id + ")' style='cursor:pointer;color:blue'><nobr>" + strconv.Itoa(v.ActionsImageID) + "</nobr></td>"
		out += "<td class='table_cell' title='Информация по клику'  onClick='show_next_actions(" + strconv.Itoa(v.NextID) + ")' style='cursor:pointer;color:blue'><nobr>" + strconv.Itoa(v.NextID) + "</nobr></td>"
		//>"+strconv.Itoa(v.NextID)+"</td>";
		out += "<td class='table_cell' >" + strconv.Itoa(v.Energy) + "</td>"
		var usefulness = strconv.Itoa(v.Usefulness)
		//if limitBasicID==5{
		if v.Usefulness < 0 {
			usefulness = "<span " + sStyle + " onClick='cliner_block(" + strconv.Itoa(v.ID) + ")' title='разблокировать (установить Полезность в 1)'><b>" + usefulness + "</b></span>"
		}
		out += "<td class='table_cell' >" + usefulness + "</td>"
		out += "<td class='table_cell' >" + strconv.Itoa(v.Belief) + "</td>"
		out += "<td class='table_cell' >" + strconv.Itoa(v.Count) + "</td>"
		out += "</tr>"
		count++
		if limitBasicID == 0 && count > 100 {
			break
		}
	}
	out += "</table>"
	return out
}

////////////////////////////////////

// показать пусковой стимул, к которому привязан автоматизм
func GetStrnameFromobjectID(objectID int) string {
	if objectID > 1000000 && objectID < 2000000 { // это - действия кнопок с пульта
		imgID := objectID - 1000000
		act := ActivityFromIdArr[imgID]
		if act == nil {
			return ""
		}
		out := ""
		for i := 0; i < len(act.ActID); i++ {
			if i > 0 {
				out += ", "
			}
			out += termineteAction.TerminalActonsNameFromID[act.ActID[i]]
		}
		return "Пусковые действия:<br><br><b>" + out + "</b>"
	}
	if objectID > 2000000 { // это - фраза
		imgID := objectID - 2000000
		return "Пусковая фраза:<br><br><b>" + GetPraseStringsFromVerbalID(imgID) + "</b>"
	}
	return ""
}

/////////////////////////////////////

/*
	Список фраз, для которых есть автоматизм Beast в этих условиях.

Для иконки http://go/img/words.png нал полем ввода.
*/
func GetAutomatizmPraseList(basicID int, contexts string) string {
	out := ""
	nCol := 0
	// образ эмоции
	var contextsArr []int
	cArr := strings.Split(contexts, ",")
	for i := 0; i < len(cArr); i++ {
		cID, _ := strconv.Atoi(cArr[i])
		contextsArr = append(contextsArr, cID)
	}
	emID, _ := createNewBaseStyle(0, contextsArr, true)
	// ШТАТНЫЕ автоматизмы, прикрепленные к ID узла Дерева
	var outArr []string
	for _, v := range AutomatizmBelief2FromTreeNodeId {
		//		brnch:=AutomatizmTreeFromID[v.BranchID]
		brnch, ok := ReadeAutomatizmTreeFromID(v.BranchID)
		if !ok {
			continue
		}
		if brnch.BaseID != basicID && brnch.EmotionID != emID {
			continue
		}
		if brnch.PhraseID != 0 {
			prase := GetPraseStringsFromVerbalID(brnch.PhraseID)
			//			prase:=word_sensor.GetPhraseStringsFromPhraseID(brnch.PhraseID)
			if len(prase) > 1 {
				if !lib.ExistsValInStringArr(outArr, prase) {
					outArr = append(outArr, prase)
				}
				//	}
			}
		}
	}
	if len(outArr) > 0 {
		sort.Strings(outArr)
		nCol = 0
		out = "<b>Штатные автоматизмы:</b> <table border=0 style='width:800px;font-size:14px;'><tr>"
		for i := 0; i < len(outArr); i++ {
			if !lib.ExistsValInStringArr(outArr, outArr[i]) {
				outArr = append(outArr, outArr[i])
			}
			if nCol == 6 {
				out += "</tr><tr>"
				nCol = 0
			}
			out += "<td align='left' style='cursor:pointer;' onClick='insert_pult_word(`" + outArr[i] + "`)'><nobr>" + outArr[i] + "</nobr></td>"
			nCol++
		}
		out += "</tr></table><hr>"
	}

	//автоматизмы, привязанные к ID фразе PhraseID и тогда их branchID начинается с 2000000
	outArr = nil
	out2 := ""
	for praaseID, _ := range AutomatizmIdFromPhraseId {
		prase := GetPraseStringsFromVerbalID(praaseID)
		if len(prase) > 1 {
			if !lib.ExistsValInStringArr(outArr, prase) {
				outArr = append(outArr, prase)
			}
		}
	}
	if len(outArr) > 0 {
		sort.Strings(outArr)
		nCol = 0
		out2 = "<span title='Эти автоматизмы срабаотывают по фразам, отвечая на них.'><b>Общие автоматизмы:</b></span> <table border=0 style='width:800px;font-size:14px;'><tr>"
		for i := 0; i < len(outArr); i++ {
			if !lib.ExistsValInStringArr(outArr, outArr[i]) {
				outArr = append(outArr, outArr[i])
			}
			if nCol == 6 {
				out2 += "</tr><tr>"
				nCol = 0
			}
			out2 += "<td align='left' style='cursor:pointer;' onClick='insert_pult_word(`" + outArr[i] + "`)'><nobr>" + outArr[i] + "</nobr></td>"
			nCol++
		}
		out2 += "</tr></table>"
		out += out2
	}

	return out
}

//////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatizm_mirroring.go
____________________________________________
/* Формирование зеркальных автоматизмов */

package psychic

import (
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
	"strings"
)

// Формирование зеркальных автоматизмов на основе списка ответов lib/mirror_reflexes_basic_phrases/...
// тестирование - запуск из psychic.go
func FormingMirrorAutomatizmFromList(file string) string {
	path := lib.GetMainPathExeFile()
	strArr, _ := lib.ReadLines(path + file)
	// triggPhrase|baseID|ContID_list|answerPhrase|Ton,Mood|actions1,...
	if len(strArr) < 2 {
		return "Пустой файл"
	}
	/* Эти автоматизмы привязываются к baseID|ContID_list|0|, т.е. к нулевому образу пусковых
	и к нулевому тону-настроению 90.
	Но TODO: сделать более мягкую активацию автоматизмов дерева:
		 ID|ParentNode|BaseID|EmotionID|ActivityID|ToneMoodID|SimbolID|PhraseID
		 Если нет автоматизма для данного узла > ActivityID то смотреть для других узлов, начиная с данного уровня.
	Т.е. если автоматизм привязан к ToneMoodID==90 а активировалась ветка с ToneMoodID==12 где нет автоматизма,
	то пусть бы срабатывал привязанный к ToneMoodID==90 !!!
		 НО ОСТОРОЖНО (понизить силу?)
	*/

	// первую строку пропускаем из-за #utf8 bom
	for n := 1; n < len(strArr); n++ {
		if len(strArr[n]) < 10 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		// УСЛОВИЯ ДЕРЕВА
		// базовое состояние
		baseID, _ := strconv.Atoi(p[1])

		// базовые контексты
		pn := strings.Split(p[2], ",")
		var lev2 []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				lev2 = append(lev2, b)
			}
		}

		// образ отсуствия тона и настроения
		tm := 90

		// засунуть фразу в дерево слов и дерево фраз
		prase := p[0]
		wordSensor.VerbalDetection(prase, 0, 0, 0)
		PhraseID := wordSensor.CurrentPhrasesIDarr

		// первый символ ответной фразы
		FirstSimbolID := wordSensor.GetFirstSymbolFromPraseID(PhraseID)
		// создать образ Брока
		_, verbal := CreateVerbalImage(FirstSimbolID, PhraseID, 0, 0)

		nodeID := FindConditionsNode(baseID, lev2, nil, FirstSimbolID, tm, verbal.ID)
		/* если есть привязанный к узлу автоматизм, то он просто перестанет быть штатным,
		т.к. авторитерный (зеркальный) автоматизм важнее
		exists:=ExistsAutomatizmForThisNodeID(nodeID)
		if exists {
			continue
		}	*/
		if nodeID > 0 {
			// создать автоматизм и привязать его к nodeID
			//var sequence = "Snn:" // ответная фраза
			// засунуть фразу в дерево слов и дерево фраз
			wordSensor.VerbalDetection(p[3], 0, 0, 0)
			answerID := wordSensor.CurrentPhrasesIDarr

			//sequence += "|Тnn:" // тон и настроение
			tnArr := strings.Split(p[4], ",")
			t, _ := strconv.Atoi(tnArr[0])
			m, _ := strconv.Atoi(tnArr[1])

			var aArr []int
			aD := strings.Split(p[5], ",")
			for i := 0; i < len(aD); i++ {
				a, _ := strconv.Atoi(aD[i])
				aArr = append(aArr, a)
			}

			NoWarningCreateShow = true

			ActionsImageID, _ := CreateNewlastActionsImageID(0, 0, aArr, answerID, t, m, true)
			_, autmzm := CreateAtutomatizmNoSaveFile(nodeID, ActionsImageID)
			NoWarningCreateShow = false
			if autmzm != nil {
				autmzm.Usefulness = 1          //авторитарный
				SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
				// ?? autmzm.GomeoIdSuccesArr какие ID гомео-параметров улучшает это действие
			}
		}
	}
	SaveAllPsihicMemory()
	return "OK"
}

/*
	на основе общего шаблона ответов lib/mirror_basic_phrases_common.txt

Создаются автоматизмы, привязанные к пусковой фразе, а не к узлу дерева,
с BranchID > 2000000.
var AutomatizmIdFromPhraseId=make(map[int] []*Automatizm)
// тестирование - запуск из psychic.go
*/
func FormingMirrorAutomatizmFromTempList(file string) string {
	path := lib.GetMainPathExeFile()
	strArr, _ := lib.ReadLines(path + file)
	// triggPhrase|baseID|ContID_list|answerPhrase|Ton,Mood|actions1,...
	if len(strArr) == 0 {
		return "Пустой файл"
	}
	/* Эти автоматизмы привязываются к baseID|ContID_list|0|, т.е. к нулевому образу пусковых
	   и к нулевому тону-настроению 90.
	   Но TODO: сделать более мягкую активацию автоматизмов дерева:
	      ID|ParentNode|BaseID|EmotionID|ActivityID|ToneMoodID|SimbolID|PhraseID
	      Если нет автоматизма для данного узла > ActivityID то смотреть для других узлов, начиная с данного уровня.
	   Т.е. если автоматизм привязан к ToneMoodID==90 а активировалась ветка с ToneMoodID==12 где нет автоматизма,
	   то пусть бы срабатывал привязанный к ToneMoodID==90 !!!
	      НО ОСТОРОЖНО (понизить силу?)
	*/

	for n := 0; n < len(strArr); n++ {
		if len(strArr[n]) < 10 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		// УСЛОВИЯ ДЕРЕВА
		// пусковая фраза
		triggerPrase := p[0]

		// ответ
		answerPrase := p[1]

		// тон, настроение
		pt := strings.Split(p[2], ",")
		t, _ := strconv.Atoi(pt[0])
		m, _ := strconv.Atoi(pt[1])
		//tm := GetToneMoodID(t, m + 19)

		// засунуть фразу в дерево слов и дерево фраз
		wordSensor.VerbalDetection(triggerPrase, 0, 0, 0)
		triggerPraseID := wordSensor.CurrentPhrasesIDarr

		wordSensor.VerbalDetection(answerPrase, 0, 0, 0)
		answerPraseID := wordSensor.CurrentPhrasesIDarr

		// создать автоматизм и привязать его к объекту
		NoWarningCreateShow = true
		// для фразы triggerPraseID создаем привязанный к ней автоматизм
		ActionsImageID, _ := CreateNewlastActionsImageID(0, 0, nil, answerPraseID, t, m, true)
		_, autmzm := CreateAtutomatizmNoSaveFile(2000000+triggerPraseID[0], ActionsImageID)
		NoWarningCreateShow = false
		if autmzm != nil {
			autmzm.Usefulness = 1          //авторитарный
			SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
			// ?? autmzm.GomeoIdSuccesArr какие ID гомео-параметров улучшает это действие
		}
	}
	SaveAllPsihicMemory()
	return "OK"
}

/*
	создание зеркального автоматизма, повторяющего действия оператора в данных условиях

в ответ на действия sourceAtmzm - причина ответа оператора
Только что действиями оператора была активирована ветка detectedActiveLastNodID дерева и
есть информация об этих действиях в curActiveActions
Автоматизм прикрепляется к ветке предыдущей активации дерева LastDetectedActiveLastNodID (причине) -
которая становится пусковым стимулом отзеркаливания.
*/
func createNewMirrorAutomatizm(sourceAtmzm *Automatizm) {
	if sourceAtmzm == nil || curActiveActions == nil {
		return
	}
	if curActiveActions.ActID == nil && isUnrecognizedPhraseFromAtmtzmTreeActivation {
		//sourceAtmzm.Usefulness=-1
		sourceAtmzm.Usefulness = 0
		sourceAtmzm.Count = 0
		return
	}
	/* вытащить действия исходного автоматизма чтобы найти или сделать узел дерева с таким пускателем
	   и существубшими BaseID и EmotionID
	*/

	//	curNode:=AutomatizmTreeFromID[detectedActiveLastNodID]
	curNode, ok := ReadeAutomatizmTreeFromID(detectedActiveLastNodID)
	if !ok {
		return
	}
	targetNodeID := findTreeNodeFromAutomatizmActionsImage(curNode.BaseID, curNode.EmotionID, sourceAtmzm)
	if targetNodeID == 0 {
		return
	}
	//	SaveAutomatizmTree()
	// найти узел, который может реагировать на данные действия и если нет - создать его чтобы привязать зеркальный автоматизм

	// создать автоматизм и привязать его к объекту
	// NoWarningCreateShow=true
	ActionsImageID, _ := CreateNewlastActionsImageID(0, 0, curActiveActions.ActID, curActiveActions.PhraseID, curActiveActions.ToneID, curActiveActions.MoodID, true)
	_, autmzm := CreateAtutomatizmNoSaveFile(targetNodeID, ActionsImageID)
	//	NoWarningCreateShow=false
	if autmzm != nil {
		detectedActiveLastNodID = targetNodeID
		// сделать автоматизм штатным, т.к. действия авторитарно верные
		autmzm.Usefulness = 1 //авторитарный
		SetAutomatizmBelief(autmzm, 2)
		autmzm.Count = EvolushnStageAtmzCount(true) // накручиваем счетчик успешных повторов в зависимости от стадии развития
		if doWritingFile {
			SaveAutomatizm()
		}
	}
}

/*
	вытащить действия исходного автоматизма чтобы найти или сделать узел дерева с таким пускателем

и существубшими BaseID и EmotionID
*/
func findTreeNodeFromAutomatizmActionsImage(baseID int, EmotionID int, atmz *Automatizm) int {
	lev2 := EmotionFromIdArr[EmotionID].BaseIDarr

	actImage := atmz.ActionsImageID
	//ai:=ActionsImageArr[actImage]
	ai, ok := ReadeActionsImageArr(actImage)
	if !ok {
		return 0
	}

	// первый символ ответной фразы. В случае ответной цепочки это первый символ первого слова цепочки
	simbolID := wordSensor.GetFirstSymbolFromPraseID(ai.PhraseID)

	/* настроение при передаче фразы с Пульта:
	   	20-Хорошее    21-Плохое    22-Игровое    23-Учитель    24-Агрессивное   25-Защитное    26-Протест
	   ID возникает при добавлении 19 к номеру радиокнопки пульта, например, для Хорошее 1+19=20
	*/
	tm := GetToneMoodID(ai.ToneID, ai.MoodID+19)

	prase := 0
	var actSum []int
	if ai.PhraseID != nil {
		prase = ai.PhraseID[0]
	}
	if atmz.NextID == 0 {
		actSum = append(actSum, ai.ActID...)
	} else {
		var praseSum []int
		lib.MapFree(MapGwardAutomatizmNextStringFromID)
		arr := AutomatizmNextStringFromID[atmz.NextID].next
		praseSum = append(praseSum, prase)
		actSum = append(actSum, ai.ActID...)
		for i := 0; i < len(arr); i++ {
			actImg, ok := ReadeActionsImageArr(arr[i])
			if !ok {
				continue
			}
			//if actImg, ok:=ActionsImageArr[arr[i]]; ok{
			actSum = append(actSum, actImg.ActID...)
			praseSum = append(praseSum, actImg.PhraseID...)
			//}
		}
		var wordsArr []int
		// получаем слова из всех фраз
		for i := 0; i < len(praseSum); i++ {
			wa := wordSensor.GetWordArrFromPhraseID(praseSum[i])
			if wa != nil {
				wordsArr = append(wordsArr, wa...)
			}
		}
		// создаем новую фразу из слов
		str := wordSensor.PhraseDetection(wordsArr, false)
		if len(str) > 0 {
			prase = wordSensor.DetectedUnicumPhraseID
		}
	}
	FirstSimbolID := wordSensor.GetFirstSymbolFromPraseID([]int{prase})
	_, verbal := CreateVerbalImage(FirstSimbolID, []int{prase}, ai.ToneID, ai.MoodID+19)
	nodeID := FindConditionsNode(baseID, lev2, actSum, tm, simbolID, verbal.ID)
	if nodeID > 0 {
		return nodeID
	}

	return 0
}

////////////////////////////////////////////////////////

// чтобы не повторять ответ еще раз после каждого игнорирования
var oldProvokatorAutomatizm *Automatizm

/*
	в случае отсуствия автоматизма в данных условиях - послать оператору те же стимулы, чтобы посмотреть его реакцию.

Создание автоматизма, повторяющего действия оператора в данных условиях
*/
func provokatorMirrorAutomatizm(sourceAtmzm *Automatizm, purposeGenetic *PurposeGenetic) {
	if sourceAtmzm == nil || purposeGenetic == nil {
		return
	}

	ActionsImageID, _ := CreateNewlastActionsImageID(0, 0, curActiveActions.ActID, curActiveActions.PhraseID, curActiveActions.ToneID, curActiveActions.MoodID, true)
	// NoWarningCreateShow=true
	// для фразы triggerPraseID создаем привязанный к ней автоматизм
	_, autmzm := CreateAtutomatizmNoSaveFile(detectedActiveLastNodID, ActionsImageID)
	// NoWarningCreateShow=false
	if autmzm != nil {
		oldProvokatorAutomatizm = autmzm
		//autmzm.BranchID += linkID // не привязывать к узлу
		autmzm.Usefulness = 1          // авторитарная полезность
		SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным, т.к. действия авторитарно верные (копируем действия оператора)

	}
	// и тут же запустить реакцию с ожиданием ответа
	setAutomatizmRunning(autmzm, purposeGenetic)
}

//////////////////////////////////

____________________________________________

BOT/brain/psychic/automatizm_next.go
____________________________________________
/* Automatizm.NextID - поддержка вторичных автоматизмов цепочки, запускаемой от первого автоматизма.
Это - аналог природных программ действия, запускаетмых по пусковому стимулу.
Next - следующий автоматизм в цепочке исполнения.
Цепь запускается автоматически при запуске первого автоматизма из func GetAutomotizmActionsString()
Цепь может быть пройдена прогностически (чтобы узнать каким действием заканчивается), ментально, без выполнения ее автоматизмов,
для этого не вызывается моторное выполнение, а просто - проход цепочки с просмотром ее звеньев
или цепь может быть прервана осознанно.

В реальности есть цепочки последовательности действий, но строго по модальности,
например, последовательности мышечных сокращений. Это дает возможность моментального прогноза,
чем закончилось все действие и возможность осознанно корректировать любую фазу действий.
Вот что-то такое и нужно реализовать.
Хотя это - цепочка действий, но у в структуре действий нет NextID
и автоматизм полностью обеспечивает последовательность действий.

Так что AmtzmNextString - это просто продолжение действия в цепочке.
Для AmtzmNextString Energy наследуется от родителя-автоматизма
	и поэтому к AmtzmNextString ПОКА не применяется мозжечковый рефлекс.

Для общих автоматизмов (.BranchID>1000000) AmtzmNextString применять не следует как и для if EvolushnStage < 4 {

Чаще всего цепочка AmtzmNextString должно появляться в состоянии обучения при отзеркаливании чудого действия
с момента его предудыщего действия.
Но может быть добавлено в ходе эксперимента в состоянии любопытства.
В обоих случаях нужно использовать infoFunc22().
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

///////////////////////////////////////////////

type AmtzmNextString struct {
	ID   int
	next []int // цепочка ID ActionsImage
}

var AutomatizmNextStringFromID = make(map[int]*AmtzmNextString)
var MapGwardAutomatizmNextStringFromID = lib.RegNewMapGuard()

///////////////////////////////////////

/* создать новый AmtzmNextString
 */
var lastAmtzmNextStringID = 0 // ID последнего созданного AmtzmNextString
func createAmtzmNextStringID(id int, ActionsImageIDArr []int, CheckUnicum bool) (int, *AmtzmNextString) {
	// не разрешать создавать цепочки из нулевых образов действий
	if lib.ExistsValInArr(ActionsImageIDArr, 0) {
		return 0, nil
	}
	// убрать дублеры в массиве
	ActionsImageIDArr = lib.UniqueArr(ActionsImageIDArr)

	if CheckUnicum {
		oldID, oldVal := checkUnicumAmtzmNextString(ActionsImageIDArr)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastAmtzmNextStringID++
		id = lastAmtzmNextStringID
	} else {
		if lastAmtzmNextStringID < id {
			lastAmtzmNextStringID = id
		}
	}

	var node AmtzmNextString
	node.ID = id
	node.next = ActionsImageIDArr

	lib.MapCheckWrite(MapGwardAutomatizmNextStringFromID)
	AutomatizmNextStringFromID[id] = &node
	lib.MapFree(MapGwardAutomatizmNextStringFromID)

	return id, &node
}

//////////////////////////////////////////
/* Функцию можно использовать для выборки ActionsImageIDArr по ActionsImageID
Вернет оригинал из всех найденных клонов, т.к. у клона всегда ID больше оригинала.
*/
func checkUnicumAmtzmNextString(ActionsImageIDArr []int) (int, *AmtzmNextString) {
	if AutomatizmFromId == nil {
		return 0, nil
	}
	minID := 1000000000
	var nS *AmtzmNextString
	lib.MapCheckBlock(MapGwardAutomatizmNextStringFromID)
	for _, v := range AutomatizmNextStringFromID {
		if v == nil {
			continue
		}
		if !lib.EqualArrs(ActionsImageIDArr, v.next) {
			continue
		}
		//return v.ID,v
		if v.ID < minID {
			minID = v.ID
			nS = v
		}
	}
	lib.MapFree(MapGwardAutomatizmNextStringFromID)
	if nS != nil {
		return nS.ID, nS
	}
	return 0, nil
}

////////////////////////////////////////////

/*
	создать новую цепочку на основе существующей - клонировать

клоны создаются для удлинения и становятся не похожи на оригинал,
но некоторое время они тождестенны по next
*/
func createCloneNextStringFromID(id int) (int, *AmtzmNextString) {
	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	nS, ok := AutomatizmNextStringFromID[id]
	if !ok {
		return 0, nil
	}
	return createCloneNextString(nS)
}

// создать новую цепочку на основе существующей - клонировать
func createCloneNextString(nstr *AmtzmNextString) (int, *AmtzmNextString) {
	lastAmtzmNextStringID++
	id := lastAmtzmNextStringID
	var node AmtzmNextString
	node.ID = id
	node.next = nstr.next

	lib.MapCheckWrite(MapGwardAutomatizmNextStringFromID)
	AutomatizmNextStringFromID[id] = &node
	lib.MapFree(MapGwardAutomatizmNextStringFromID)

	return id, &node
}

///////////////////////////////////////////

// найти цепочку AmtzmNextString с таким действием, если нет - создать, если есть создать новый клон
func createClonForActins(act []int) int {
	id, nS := checkUnicumAmtzmNextString(act)
	if id > 0 {
		id, _ = createCloneNextString(nS)
	} else {
		id, _ = createAmtzmNextStringID(0, act, true)
	}
	return id
}

////////////////////////////////////////////////

// СОХРАНИТЬ структура записи: id|ActionsImage[]
func SaveAmtzmNextString() {
	var out = ""
	lib.MapCheckBlock(MapGwardAutomatizmNextStringFromID)
	for k, v := range AutomatizmNextStringFromID {
		out += strconv.Itoa(k) + "|"
		for i := 0; i < len(v.next); i++ {
			out += strconv.Itoa(v.next[i]) + ","
		}
		out += "\r\n"
	}
	lib.MapFree(MapGwardAutomatizmNextStringFromID)
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/automatizm_next.txt", out)
}

// ЗАГРУЗИТЬ структура записи: id|ActionsImage[]
func loadAmtzmNextString() {
	AutomatizmNextStringFromID = make(map[int]*AmtzmNextString)

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/automatizm_next.txt")
	if strArr == nil {
		return
	}
	for n := 0; n < len(strArr); n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		s := strings.Split(p[1], ",")
		var next []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			ai, _ := strconv.Atoi(s[i])
			next = append(next, ai)
		}
		createAmtzmNextStringID(id, next, false) // без проверки на уникальность
	}
	return
}

/////////////////////////////////////////////////////////////

/* Создать новую цепочку AmtzmNextString с привязкой к Automatizm.NextID
 */
func createNextAutomatizm(NextID int, ActionsImageID []int, CheckUnicum bool) (int, *AmtzmNextString) {

	//parent,ok:=AutomatizmFromId[NextID]
	parent, ok := ReadeAutomatizmFromId(NextID)
	if !ok {
		lib.TodoPanic("Родитель для создания вторичного автоматизма с ID=" + strconv.Itoa(NextID) + " не существует!")
	}
	if parent.BranchID > 1000000 || EvolushnStage < 4 {
		return 0, nil
	}

	idN, am := createAmtzmNextStringID(0, ActionsImageID, true)

	parent.NextID = idN // дорастили цепочку
	wasCreateNextAtmtztID = parent.ID

	return idN, am
}

////////////////////////////////////////////////////////////

/* Добавить к цепочке AmtzmNextString автоматизма Automatizm.NextID новое звено образа действия
 */
func addNextAutomatizm(atmztmID int, ActionsImageID []int, CheckUnicum bool) (int, *AmtzmNextString) {

	//parent,ok:=AutomatizmFromId[atmztmID]
	parent, ok := ReadeAutomatizmFromId(atmztmID)
	if !ok {
		lib.TodoPanic("Родитель для создания вторичного автоматизма с ID=" + strconv.Itoa(atmztmID) + " не существует!")
	}
	if parent.BranchID > 1000000 || EvolushnStage < 4 {
		return 0, nil
	}

	idN, am := createAmtzmNextStringID(0, ActionsImageID, true)

	parent.NextID = idN // дорастили цепочку
	wasCreateNextAtmtztID = parent.ID

	return idN, am
}

////////////////////////////////////////////////////////////

/*
	Добавить к цепочке AmtzmNextStringID новое звено образа действия

если еще нет AmtzmNextStringID, то сначала создать.
*/
func addAmtzmNextString(AmtzmNextStringID int, ActionsImageID []int) (int, *AmtzmNextString) {
	if AmtzmNextStringID == 0 {
		sN, am := createAmtzmNextStringID(0, ActionsImageID, true)
		return sN, am
	}
	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	sN, ok := AutomatizmNextStringFromID[AmtzmNextStringID]
	if !ok {
		sN, am := createAmtzmNextStringID(0, ActionsImageID, true)
		return sN, am
	}
	for i := 0; i < len(ActionsImageID); i++ {
		sN.next = append(sN.next, ActionsImageID[i])
	}
	// убрать дублеры в массиве
	sN.next = lib.UniqueArr(sN.next)

	return sN.ID, sN
}

////////////////////////////////////////////////////////////

/*
	Вставить в цепочку Automatizm.NextID новой образ действия, - в любую часть цепочки.

При pos==-1 вставляется в конец. При pos==0 - в начало.
*/
func insertImageActionToAmtzmNextID(atmztmID int, pos int, imgActID int) {

	//	parent,ok:=AutomatizmFromId[atmztmID]
	parent, ok := ReadeAutomatizmFromId(atmztmID)
	if !ok {
		lib.TodoPanic("Родитель для создания вторичного автоматизма с ID=" + strconv.Itoa(atmztmID) + " не существует!")
	}
	if parent.BranchID > 1000000 || EvolushnStage < 4 {
		return
	}
	if parent.NextID == 0 { // еще нет, создать next и првязать есдинственное звено (любая позиция pos)
		createNextAutomatizm(parent.ID, []int{imgActID}, true)
		return
	}

	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	nArr, ok := AutomatizmNextStringFromID[parent.NextID]
	if !ok {
		return
	}
	insertImageActionToAmtzmNext(nArr, pos, imgActID)
}
func insertImageActionToAmtzmNext(next *AmtzmNextString, pos int, imgActID int) {
	if pos == -1 || pos >= len(next.next) {
		next.next = append(next.next, imgActID)
		return
	}
	nArr := next.next
	var newArr []int
	for i := 0; i < len(nArr); i++ {
		if i == pos {
			newArr = append(newArr, imgActID)
		}
		newArr = append(newArr, nArr[i])
	}
	next.next = newArr
}

////////////////////////////////////////////////////////////

/*
	удалить образ действия с позицией в цепочке pos

При pos==-1 - последний. При pos==0 - первый.
*/
func deleteImageActionToAmtzmNextID(nextID int, pos int) {
	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	nArr, ok := AutomatizmNextStringFromID[nextID]
	if !ok {
		return
	}
	deleteImageActionToAmtzmNext(nArr, pos)
}
func deleteImageActionToAmtzmNext(next *AmtzmNextString, pos int) {
	if pos == -1 || pos >= (len(next.next)-1) {
		next.next = next.next[:len(next.next)-1] // удалить последний элемент
		return
	}
	if pos == 0 {
		next.next = next.next[1:] // удалить первый элемент
		return
	}
	nArr := next.next
	var newArr []int
	for i := 0; i < len(nArr); i++ {
		if i == pos {
			continue
		}
		newArr = append(newArr, nArr[i])
	}
	next.next = newArr
}

////////////////////////////////////////////////////////////////

/*
	создать автоматизм из цепочки действий

м.б., что в этой ветке уже есть автоматизм с действием, не даполненный NextID... он дополнится.
*/
func createAutomatizmFromNextString(next []int, branchID int) (int, *Automatizm) {
	if len(next) < 2 { // только одно действие
		return 0, nil
	}
	// создать автоматизм из первой акции
	id, atmtzm := createNewAutomatizmID(0, branchID, next[0], true)
	if len(next) > 1 {
		// создать AmtzmNextString из остальной части действий
		nOstID, _ := createAmtzmNextStringID(0, next[1:], true)
		// остальная часть действий
		atmtzm.NextID = nOstID
	}

	return id, atmtzm
}

/////////////////////////////////////////////////////////////////

/* создать AmtzmNextString из всех действий автоматизма
 */
func createNextStringFromAutomatizm(atmtzm *Automatizm) (int, *AmtzmNextString) {
	var n []int
	n = append(n, atmtzm.ActionsImageID)
	if atmtzm.NextID > 0 {
		lib.MapCheck(MapGwardAutomatizmNextStringFromID)
		nArr := AutomatizmNextStringFromID[atmtzm.NextID]
		for i := 0; i < len(nArr.next); i++ {
			n = append(n, nArr.next[i])
		}
	}
	// создать AmtzmNextString из остальной части действий
	id, nObj := createAmtzmNextStringID(0, n, true)

	return id, nObj
}

/////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
/* Запуск действия Automatizm.NextID в цепочке,
для func RumAutomatizm()
*/

func showNextAtmtzmAction(atmzmID int, NextID int, Energy int) string {
	out := showVolutionAction(NextID, Energy)
	lastRunVolutionAction = nil // не использовать механизм периода ожидания автоматизма
	return out
}

/////////////////////////////////////////////

////////////
/* произвольный запуск действий
приводит к периоду ожидания LastRunAutomatizmPulsCount и оценке результата.
*/
//lastRunVolutionAction очищается после периода ожидания:
var lastRunVolutionAction *AmtzmNextString // последняя запущенная цепочка на исполнение.

var lastRunVolutionPulsCount = 0
var prevLastRunVolutionAction *AmtzmNextString // предпоследняя запущенная цепочка на исполнение - для ученического Правила
var prevLastRunVolutionPulsCount = 0

/*
	Совершение произвольного действия (не автоматизм, а предшествующая альтернатива).

Запуск всех действия NextID в цепочке.
Может запускаться произвольно, как свободные действия вне автоматизма (showNextAtmtzmAction(0,AmtzmNextStringID,5)).
*/
func showVolutionAction(NextID int, Energy int) string {
	// Не запускать в период ожидания ответа оператора. Такое же при запуске автоматизма.
	if LastRunAutomatizmPulsCount > 0 {
		return ""
	}
	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	as, ok := AutomatizmNextStringFromID[NextID]
	if !ok {
		return ""
	}
	var out = ""
	for i := 0; i < len(as.next); i++ {
		if i > 0 {
			out += ""
		}

		//ai:=ActionsImageArr[as.next[i]]
		ai, ok := ReadeActionsImageArr(as.next[i])
		if !ok {
			continue
		}
		// каждый образ - в одну строку
		if ai.ActID != nil {
			out += TerminateMotorAutomatizmActions(0, ai.ActID, Energy, true) + " "
		}
		if ai.PhraseID != nil {
			out += TerminatePraseAutomatizmActions(ai.PhraseID, Energy) + " "
		}

		if ai.ToneID != 0 {
			out += " (" + getToneStrFromID(ai.ToneID)
		}

		if ai.MoodID != 0 {
			out += " " + getMoodStrFromID(ai.MoodID) + ")"
		}
	}

	// для периода ожидания ответа оператора
	prevLastRunVolutionAction = lastRunVolutionAction
	prevLastRunVolutionPulsCount = lastRunVolutionPulsCount
	lastRunVolutionAction = as
	lastRunVolutionPulsCount = PulsCount

	LastRunAutomatizmPulsCount = PulsCount // активность произвольного действия в чисде пульсов
	LastDetectedActiveLastNodID = detectedActiveLastNodID

	return out
}

/////////////////////////////////////////////

////////////////////////////////////////////

/*
	действия с цепочкой при плохом эффекте ВРЕМЕННО-ПРЕДПОЛОЖИТЕЛЬНАЯ ВЕРСИЯ

Пока что создать дубль автоматизма из первого в цепочке, но с NextID=0 и сделать его штатным с полезностью 0.
Полезность и коунтер - начальные (==0).

Результат оценивается в func calcAutomatizmResult() с записью 2 видов Правил.
*/
var wasCreateNextAtmtztID = 0

func badEffectChainAtmtzm(am *Automatizm) {

	/* если стало плохо после добавления нового звена, то
	 */
	if wasCreateNextAtmtztID == am.ID {
		//сделать дубликат для штатного
		_, dAm := createDuplicateAutomatizm(am.BranchID, am)
		// в дубликате привязкать цепочку Neat
		dAm.NextID = am.NextID
		// в дубликате  удалить последнее звено Next
		deleteImageActionToAmtzmNextID(dAm.NextID, -1)
		// сделать автоматизм штатным
		SetAutomatizmBelief(dAm, 2)
	}

	// TODO - пока больше ничего
}

/////////////////////////////////////////////////

// /////////////////////////////////////////////////
// найти готовый или создать новый автоматизм из AmtzmNextString.ID
func createAndRunAutomatizmFromAmtzmNextString(actionID int) *Automatizm {
	// на всякий случай убережемся, что акция - именно типа AmtzmNextString
	if actionID < prefixActionIdValue { // НЕ последовательность образов действий AmtzmNextString.ID
		return nil
	}
	nID := actionID - prefixActionIdValue // чистый ID без префикса
	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	nextString, ok := AutomatizmNextStringFromID[nID]
	if !ok { // такого не должно быть, НО...
		return nil
	}
	_, atmtzm := createAutomatizmFromNextString(nextString.next, detectedActiveLastNodID)
	return atmtzm
}

/////////////////////////////////////////////////////

// //////////////////////////////////////////////
// выдать на пульт инфу о цепочке действий
func GetNextActionsInfo(nextID int) string {
	if nextID == 0 {
		return "Отсутствует цепочка действий."
	}

	lib.MapCheck(MapGwardAutomatizmNextStringFromID)
	as, ok := AutomatizmNextStringFromID[nextID]
	if !ok {
		return ""
	}
	var out = "<b>Цепочка действий:</b> "
	for i := 0; i < len(as.next); i++ {
		if i > 0 {
			out += "<br>"
		}
		out += getSingleAtcStr(as.next[i])
	}
	return out
}

/////////////////////////////////////////

func GetNextActionsInfoList() string {
	var out = ""
	lib.MapCheckBlock(MapGwardAutomatizmNextStringFromID)
	for k, v := range AutomatizmNextStringFromID {
		if v == nil {
			continue
		}
		out += " <span style='cursor:pointer;color:blue' onClick='show_next_actions(" + strconv.Itoa(k) + ")'>ID=<b>" + strconv.Itoa(k) + "</b></span> Действия цепочки: "
		for i := 0; i < len(v.next); i++ {
			out += strconv.Itoa(v.next[i]) + ","
		}
		out += "<br>\r\n"
	}
	lib.MapFree(MapGwardAutomatizmNextStringFromID)
	return out
}

//////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/automatizm_result.go
____________________________________________
/* Ожидание результата запущенного автоматизма и его обработка

В BAD_detector.go в самом низу есть func BetterOrWorseNow() с комментариями по делу. Я ее отрабатывал как раз для того, чтобы фиксировать любые улучшения или ухудшения для определения эффекта автоматизма.
Она вызывается (через трансформатор против цицличности wasChangingMoodCondition()) 2 раза: в момент запуска автоматизма и как только совершится любое действие оператора на пульте. Таким образом в automatizm_result.go получается дифферент:
oldlastBetterOrWorse,oldBetterOrWorse,oldParIdSuccesArr = wasChangingMoodCondition()
Т.е. если ты поставишь точку прерывания на
oldlastBetterOrWorse,oldBetterOrWorse,oldParIdSuccesArr = wasChangingMoodCondition()
то и получишь эффект автоматизма.
*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
	"strconv"
)

//////////////////////////////////////////////////////////////////
/* Длина группового правила.
Накручивается при каждом стимуле в activeGameMode(), сбрасывается в automatizmActionsPuls() и StopWaitingWeriodFromOperator() - принудительно по пашке на пульте или по истечении периода ожидания.
то есть глубина группового правила зависит от времени удержания образа. Это позволяет записывать в группу только события, которые были в периоде удержания, а не все подряд.
В функциях GPT лимит устанавливается отдельно.

ПРАКТИЧЕСКИ НЕ ИСПОЛЗУЕТСЯ...
*/
//var LimitGroupRules int

////////////////////////////////////////////////////////////////

/*
	Это используется для определения момента реакция оператора Пульта на действия автоматизма.

За 20 сек г.параметры могли бы просто натечь и вызывать сработавание при ожидании ответной реакции.
Флаг сбрасывается через пульс после запуска автоматизма.
*/
var WasOperatorActiveted = false

var WasConditionsActiveted = false

var savePsyBaseMood = 0 // -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
// для более точной оценки
var savePsyMood = 0 //сила Плохо -10 ... 0 ...+10 Хорошо
// НОВИЗНА СИТУАЦИИ сохраняется значение CurrentAutomatizTreeEnd[] для решений
var savedNoveltySituation []int

// отслеживание запущенных автоматизмов
// структура примитивных целей, создающих контекст ситуации НЕ СБРАСЫВАЕТСЯ после ожидания
var savePurposeGenetic *PurposeGenetic

/* При запуске автоматизма определяются:
// момент запуска автоматизма в числе пульсов
var LastRunAutomatizmPulsCount =0 //сбрасывать ожидание результата автоматизма если прошло 20 пульсов
// ожидается результат запущенного MotAutomatizm
var LastAutomatizmWeiting *Automatizm //сбрасывается указатель автоматизма
*/

func setAutomatizmRunning(am *Automatizm, ps *PurposeGenetic) {
	lib.WritePultConsol("<span style='color:blue'>Ожидание ответа оператора.</span>")

	// при срабатывании автоматизма - блокируются все рефлексторные действия
	//MotorTerminalBlocking=true
	notAllowReflexRuning = true //уже есть, но на всякий случай :)

	LastAutomatizmWeiting = am                            // уже есть, но для надежности :)
	LastDetectedActiveLastNodID = detectedActiveLastNodID // уже есть, но для надежности :)

	savePsyBaseMood = PsyBaseMood
	savePsyMood = PsyMood
	savedNoveltySituation = NoveltySituation
	if ps != nil {
		savePurposeGenetic = ps
	}
	WasOperatorActiveted = false // ждем ответа оператора
	// зафиксировать текущее состояние на момент срабатывания автоматизма
	//!!!! oldlastBetterOrWorse,oldParIdSuccesArr = wasChangingMoodCondition()
	gomeostas.BetterOrWorseInit()

	CurrentInformationEnvironment.IsWaitingPeriod = true
	notAllowReflexRuning = false
}

func clinerAutomatizmRunning() {
	//MotorTerminalBlocking=false
	notAllowReflexRuning = false

	LastAutomatizmWeiting = nil // func RumAutomatizm()
	lastRunVolutionAction = nil // func showVolutionAction

	LastRunAutomatizmPulsCount = 0
	WasOperatorActiveted = false
	onliOnceWasConditionsActiveted = false
	// !!!! НЕ СБРАСЫВАТЬ savePurposeGenetic=nil - он может определяться независимо от запуска автоматизма

	CurrentInformationEnvironment.IsWaitingPeriod = false

	wasRunProvocationFunc = false

	// только в func saveNewMentalEpisodic очищать!   clinerFuncSequence()
}

func StopWaitingWeriodFromOperator() {
	setInterruptionEpisosde() //вставить пустой кадр эпиз.памяти - прервать тему
	clinerAutomatizmRunning()
	/*
		if !IsArbitraryGameMode {
			transfer.IsPsychicGameMode = false
			LimitGroupRules = -1
		}	*/
}

// ////////////////////// ПУЛЬС
// ПУЛЬС срабатывания по каждому Пульсу здесь для удобства
var oldBetterOrWorse = 0     //- стали лучше или хуже: величина измнения от -10 через 0 до 10
var oldParIdSuccesArr []int  //стали лучше следующие г.параметры []int гоменостаза
var oldlastBetterOrWorse = 0 // насколько изменилось общее состояние, значение от  -10(максимально Плохо) через 0 до 10(максимально Хорошо)
func automatizmActionsPuls() {

	if LastRunAutomatizmPulsCount == 0 {
		return
	}
	// вышло время ожидания реакции
	if (LastRunAutomatizmPulsCount + WaitingPeriodForActionsVal) < PulsCount {
		/*
			if !IsArbitraryGameMode {
				transfer.IsPsychicGameMode = false
				LimitGroupRules = -1
			}*/
		// если просто позволить отработать noAutovatizmResult() при закрытии func13(), то принудительное обнуление времени ожидания в
		// main/getParams := r.FormValue("get_params") приведет к тому, что тут же сработает noAutovatizmResult() и автоматизм запустится 2 раза подряд
		// поэтому блокируем это дело - не позволяем двойную активацию после отработки func13()
		if IsEndWaitPeriodFunc13 {
			IsEndWaitPeriodFunc13 = false
		} else {
			// отреагировать на отсуствие реакции - повторить автоматизм с большей силой Energy
			// Из МОЗЖУЧКА как-то отреагировать на отсуствие реакции - повторить автоматизм с большей силой Energy
			if noAutovatizmResult() { // была попытка отреагировать сильнее - в cerebellum.go
				return // чтобы не сбрасывать clinerAutomatizmRunning()
			}
		}

		//сбрасывать ожидание результата автоматизма если прошло WaitingPeriodForActionsVal пульсов
		clinerAutomatizmRunning()
		//вставить пустой кадр эпиз.памяти - прервать тему
		setInterruptionEpisosde()

		if oldBetterOrWorse < 0 { // Плохо и оператор не ответил в течение периода ожидания на важный запрос
			// Новая тема мышления
			runNewTheme(7, 2)
		}
	}
}

// отреагировать на отсуствие реакции - повторить автоматизм с большей силой Energy при EvolushnStage <= 3
func noAutovatizmResult() bool {

	if EvolushnStage > 3 {
		// осмыслить ситуацию - Активировать Дерево Понимания
		understandingSituation(1)
		clinerAutomatizmRunning()
		return true
	}

	// не опасная ситуация, можно поэкспериментировать
	if EvolushnStage == 3 && !CurrentPurposeGenetic.veryActual {
		/* в случае отсуствия автоматизма в данных условиях - послать оператору те же стимулы, чтобы посмотреть его реакцию.
		   Создание автоматизма, повторяющего действия оператора в данных условиях
		НО если уже помылался provokatorMirrorAutomatizm то больше не делать этого (бесконечный цикл)
		*/
		if oldProvokatorAutomatizm != LastAutomatizmWeiting { // не повторять, если только что был такой ответ
			provokatorMirrorAutomatizm(LastAutomatizmWeiting, &CurrentPurposeGenetic)
			clinerAutomatizmRunning()
			return true
		}
	}

	// реакция была, но оператор не обратил на нее внимания, нужно усилить силу действия мозжечковым рефлексом
	if cerebellumCoordination(LastAutomatizmWeiting, 1) {
		// и тут же снова запустить реакцию!
		if oldProvokatorAutomatizm != LastAutomatizmWeiting { // не повторять, если только что был такой ответ
			setAutomatizmRunning(LastAutomatizmWeiting, &CurrentPurposeGenetic)
			clinerAutomatizmRunning()
			return true
		}
	}
	clinerAutomatizmRunning()
	return false
}

/*
сохраняет ID автоматизма до создания зеркального
актуально для 3 стадии, когда на стимул-ответ Оператора сработал существующий автоматизм
его надо отправить на пульт, а не созданный зеркальный
если же запретить вообще создавать зеркальные для таких ситуаций
тогда зеркалить можно будет только НОВЫЕ стимулы и ответы
что обедняет механизм третьей стадии
*/
var oldAtmzAfterTreeActivatedID = 0
var IsEndWaitPeriodFunc13 = false

/*
	ПОСЛЕ ОРИЕНТИРОВОЧНОГО РЕФЛЕКСА оценивать действие запущенного автоматизма

lastBetterOrWorse НЕ ИСПОЛЬЗУЕТСЯ т.к. lastCommonDiffValue более точен и информативен
*/
func calcAutomatizmResult(lastCommonDiffValue int, wellIDarr []int) {

	lib.WritePultConsol("<span style='color:blue;background-color:#FFD0FF;'>Был ОТВЕТ ОПЕРАТОРА (func calcAutomatizmResult). Мотивационный эффект: <b>" + strconv.Itoa(lastCommonDiffValue) + "</b></span>")

	/*	полезность автоматизма в третьей стадии при отзеркаливании на втором шаге ставится в createNewMirrorAutomatizm()
		при этом нельзя менять полезность автоматизма созданного в цикле отзеркаливания на первом шаге, иначе в getAutomatizmFromNodeID()
		будет выдавать как наилучший автоматизм первого шага. То есть для отзеркаливания в 3 стадии должно быть строго из за getAutomatizmFromNodeID():
		1. автоматизм, созаднный на 1 шаге - Usefulness==0
		2. автоматизм, созаднный на 2 шаге - Usefulness==1 (устанавливается currentAutomatizmAfterTreeActivatedIDв createNewMirrorAutomatizm())
		И то же самое для отработки infoFunc13()
	*/
	if EvolushnStage != 3 && curFunc13ID == 0 {
		if lastRunVolutionAction == nil { // было НЕ произвольное действие
			automatizmCorrection(LastAutomatizmWeiting, lastCommonDiffValue, wellIDarr)
		}
	}
	if lastCommonDiffValue < 0 {
		motorActionEffect = 0 // нужно думать, искать действия в func consciousnessThinking()
	}

	// в третьей стадии по умолчанию, в более высоких по факту срабатывания func13()
	// для этого в ней используется маркер curFunc13ID >0
	if EvolushnStage == 3 || curFunc13ID > 0 {
		if GetMotorsAutomatizmListFromTreeId(detectedActiveLastNodID) != nil { // если есть список всех автоматизмов для ID узла Дерева
			// сохраняем активировавшийся на стимул автоматизм
			// для случая, когда оператор дал ответ, как отвечать - а на ответ уже есть автоматизм. В этом случае бот ответит - и его ответ пойдет в запись зеркального автоматизма
			// вместо того, что указал оператор. Чтобы этого не было, надо сохранить ответ оператора.
			oldAtmzAfterTreeActivatedID = getAutomatizmFromNodeID(detectedActiveLastNodID)
		}

		/* отзеркаливание ответа оператора не зависимо от того, стало хуже или лучше
		потому, что это был ответ оператора на действия автоматизма, значит - авторитетный ответ
		Создание автоматизма, повторяющего действия оператора в данных условиях
		*/
		createNewMirrorAutomatizm(LastAutomatizmWeiting)
		/*		if curFunc13ID > 0 {
				//clinerAutomatizmRunning() // нельзя, иначе fixEpizMemoryRules() не отработает, только fixEpizMemoryTeachRules()
				endBaseIdCycle(curFunc13ID) // подвисает
				clinerFunctionsInAllCickles(13)
				IsEndWaitPeriodFunc13 = true
				curFunc13ID = 0 // закрываем маркер цикла func13
			}*/
	}

	// >3 потому, что раньше не пишется эпизодическая память и формируются более примитивные механизмы.
	if EvolushnStage > 3 {
		if !IsSleeping { //бодрствование (при сновидении или грез, мечтаний, пассивного мышления - fixDreamsRules(lastCommonDiffValue))
			/* При каждом ответе на действия оператора - прописывать текущее правило rules
			   		и делать новый кадр эпизодической памяти
			      А так же просматривать эпизод память взад макчимум на EpisodeMemoryPause шагов или до паузы в общении > 30 шагов,
			   		фиксируя цепочку правил.
			*/

			// был запуск автоматизма или произвольной цепочки действий - записать 2 вида Правил
			if lastRunVolutionAction != nil { // было произвольное действие
				// создать автоматизм из lastRunVolutionAction
				_, azm := createAutomatizmFromNextString(lastRunVolutionAction.next, LastDetectedActiveLastNodID)
				automatizmCorrection(azm, lastCommonDiffValue, wellIDarr)
			}
			// записать 2 вида Правил для автоматизма или произвольной цепочки действий
			//		stimul, _ := CreateNewlastActionsImageID(0, curActiveActions.ActID, curActiveActions.PhraseID, curActiveActions.ToneID, curActiveActions.MoodID,true)
			// образ действий оператора Стимул:	curStimulImageID и есть Ответ: curActiveActions
			fixEpizMemoryRules(lastCommonDiffValue)

			// записать учительское Правило: как Оператор отвечает на действиЯ Beast - авторитетное Правило всегда имеет +эффект
			if !wasRunProvocationFunc { // не записывать учительское правило после провокации func infoFunc31
				// не включаем в условие curActiveActionsID > 0 т.к. ID стимула будет определен в func fixEpizMemoryTeachRules
				if ActivationTypeSensor > 1 { // только стимул оператора, а не гомео
					//если оператор ответил только во время периода ожидания
					if (LastRunAutomatizmPulsCount + WaitingPeriodForActionsVal) > PulsCount {
						fixEpizMemoryTeachRules(lastCommonDiffValue)
					}
				}
			}
			wasRunProvocationFunc = false

			if EvolushnStage > 4 && lastCommonDiffValue > 0 {
				// закрыть доминанту, котора точно подходит
				checkGestalt(lastCommonDiffValue)
			}
		} //if !IsSleeping || (IsSleeping && IsDreamsProcess){//бодрствование или процесс сновидения
	}
	return
}

/*
	для индикации период ожидания реакции оператора на действие автоматизма

//   psychicWaitingPeriodForActions()
Индикация включается после появления диалога ответа на Пульте (pult_gomeo.php: var allowShowWaightStr=0;).
*/
func WaitingPeriodForActions() (bool, int) {
	//if LastRunAutomatizmPulsCount > 0 && ActivationTypeSensor > 1 {
	if LastRunAutomatizmPulsCount > 0 {
		time := WaitingPeriodForActionsVal - (PulsCount - LastRunAutomatizmPulsCount)
		return true, time
	}
	return false, 0
}

///////////////////////////////////////////////

/*
	Определение эффекта реакции по результатам в BAD_detector.go, негативный или позитивный: возвращает значение res0.

# В  gomeostas.BetterOrWorseNow() учитывается CommonMoodAfterAction - Общее (де)мотивирующее действие с Пульта

res - стали лучше или хуже: величина измнения от -10 через 0 до 10
wellIDarr - стали лучше следующие г.параметры []int гоменостаза

wasChangingMoodCondition вызывается 1 раз: как только совершится любое действие оператора на пульте (kind==2)

Информация о том, когда вызывается (kind) раньше использовалась, но теперь она пока не используется.
*/
var CurrentMoodCondition = 0 // индикация на Пульте
func wasChangingMoodCondition() (int, []int) {
	//стало хуже или лучше теперь, возвращает величину измнения от -10 через 9 до 10
	res0, wellIDarr := gomeostas.BetterOrWorseNow()
	prevStimulsEffect = globalStimulsEffect
	globalStimulsEffect = res0 // глобализуем для func saveNewEpisodic

	// передать Боль и Радость в психику
	painValue, joyValue = gomeostas.GetCurPainJoy()
	//Скорректировать настроение по результату реакции??
	// GetCurMood() наверное здесь не нужно это делать

	// если в текущем стимуле с пульта не было действия с эффектом, нужно игнорировать эффект от предыдущего действия, иначе оно передается на действие с нулевым эффектом через CommonMoodAfterAction
	// например нажали кнопку наказать, срабатал автоматизм, потом послали вербальный стимул без кнопок - и он заблокировал предыдущий автоматизм потому, что при нулевом эффекте
	// вербального стимула эффект взялся от CommonMoodAfterAction: BetterOrWorseNow() стр. 407
	if curActions.ActID == nil {
		res0 = 0
	}

	/* это надо делать через LastAutomatipmCorrection(), потому что здесь CheckCurActions() показывает,
	   что РАНЕЕ была нажата какая то кнопка и не факт, что только что, а может и несколько шагов диалога назад.
	   В итоге может получиться ситуация:
	   "привет" : [наказать] : "Хай" - и блокируются первые 2 автоматизма. Первый понятно, что эффектом кнопки, а сама кнопка потому,
	   что эффект ее нажатия сохранился в ActionFromPult[]
	   и когда пришел пускатель "Хай", здесь прочитался отрицательный эффект от кнопки и заблокировал ее саму.
	   То есть получилось, как будто вербальный автоматизм имеет плохой эффект и потому заблокировал предыдущий автоматизм.
	   поэтому в afterTreeActivation() поставлена заглушка if !action_sensor.IsPress3or4button,
	   а обработка учительских кнопок делается через LastAutomatipmCorrection()
	*/

	if curActiveActions != nil {
		// влияние значимости первого компонента фразы + влияние тона
		tone := curActiveActions.ToneID
		k := 1
		if tone == 4 { // повышенный
			k = 2
		}

		eObj, v := getObjectsImportanceValue(curActiveActions.ID, detectedActiveLastProblemNodID)
		if eObj != nil {
			if v > 0 {
				res0 = res0 + 1*k
			}
			if v < 0 {
				res0 = res0 - 1*k
			}
		}

	} //	if curActiveActions !=nil {

	// для страницы Пульта
	if res0 > 0 {
		CurrentMoodCondition = 3
	} //лучше
	if res0 == 0 {
		CurrentMoodCondition = 2
	} // не изменилось
	if res0 < 0 {
		CurrentMoodCondition = 1
	} // Хуже

	// для лога
	lib.WritePultConsol("Изменение состояния: " + strconv.Itoa(res0))

	return res0, wellIDarr
}

//////////////////////////////////////////////////////////////

/* // текущий ID пускового стимула типов curActiveActions или curBaseStateImage
при активации дерева автоматизмов. Если тип curBaseStateImage, то ID отрицательное (ID<0)!
*/
//var currentTriggerID=0 не нужен
//var currentRulesID = 0

// для установки значимости учительских кнопок -5/+5, которые обрабатываются отдельно от прочих. Иначе установится -1/+1
//var effectPress3or4button = 0

/*  !!!Нельзя делать только для кнопок такую ф-цию! кнопки могуи активироваться и с фразой! появилось множество побочек!

только при активации действием кнопок 	в игровом режиме при воздействии кнопками Наказать, Поощрить - записывать Usefulness ранее выполненного автоматизма
Вызывается только из perception.go
Не забыть потом в afterTreeActivation() поставить заглушку для calcAutomatizmResult() - иначе будет двойной вызов calcAutomatizmResult()
wasChangingMoodCondition() не подходит для

func LastAutomatipmCorrection() {
	if LastAutomatizmWeiting == nil {
		return
	}

	// LastRunAutomatizmPulsCount - время начала периода ожидания
	if (PulsCount - LastRunAutomatizmPulsCount) > (limitOfActionsAfterStimul + 10) { // прошло более limitOfActionsAfterStimul секунд
		return
	}

	// какие акции действуют в данный момент действий с пульта - активные контексты действий с Пульта Очищаются
	ActID := action_sensor.CheckCurActionsContext()

	ActivationTypeSensor = 2 // было действие

	var effect = 0
	if lib.ExistsValInArr(ActID, 3) { // наказать
		effect = -5
	}
	if lib.ExistsValInArr(ActID, 4) { // поощрить
		effect = 5
	}
	if effect != 0 {
		var gomeoParIdSuccesArr []int
		// так как обработка действий учительских кнопок происходит ДО automatizmTreeActivation() нужно здесь задать образы активностей,
		// иначе правило не запишется. См fixEpizMemoryRules() стр. 36
		curStimulImage = curActiveActions
		curStimulImageID = curActiveActionsID
		// обработать изменение состояния фиксация ПРАВИЛА, Стимул - ОТ ОПЕРАТОРА
		calcAutomatizmResult(effect, gomeoParIdSuccesArr)
		effectPress3or4button = effect
	}
}
*/
///////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/cerebellum.go
____________________________________________
/* Рефлексы мозжечка

Самый первый уровень осознания - подгонка действий под заданную цель.

Оценка результата автоматизма и корректировка мозжечковыми рефлексами ПРИ ОСОЗНАНИИ ЦЕЛИ И ПЕРЕБОРА-НЕДОБОРА,
А ТАК ЖЕ НЕОБХОДИМОСТИ ДОПОЛНИТЕЛЬНЫХ (корректирующих) ДЕЙСТВИЙ.

Если нет эффекта от фразы - усилить ее и м. б. дополнить другими действиями и фразами, фиксируя результат в рефлексе мозжечка
- примитивный уровень осмысления после активации дерева понимания.

Для каждого действия brain\reflexes\terminete_action.go задается "сила" действия в градации от 1 до 10, которая передается на Пульт словами:
максимально сильно, очень сильно, ... едва-едва.

Это - только на уровне осмысления в Дереве Понимания:
   cerebellumCoordination(LastAutomatizmWeiting.ID)
Должно быть осознание цели и перебора - недобора!

В каждом автоматизме есть параметр силы: Automatizm.Energy вот он и корректируется.
т.е. функция рефлекса мозжечка состоит в нахождении излишней или недостаточной силы по результатам действия
и поправки силы действия автоматизма getCerebellumReflexAddEnergy(automatizmID int)
Но самое полезное - выполнение дополнительных действий сразу после исходного автоматизма
по рефлексу, привязанному к исходному автоматизму: runCerebellumReflex(automatizmID int)

В частности, одна из корректирующих функций - энергосберегательная и рефлекс должен это учитывать.
т.е. рефлекс создается при повышенном расходе и уменьшает силу данной реакции.

К этому постепенно, с обучением, добавляются другие координирующие функции, которые по разным признакам
координируют выполнение реакции: cerebellumCoordination()

https://scorcher.ru/diary/topic.php?id=892&page_txt=43
*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/brain/transfer"
)

/* Создать или дополнить автоматизм рефлексом мозжечка cerebellumReflexFromID[LastAutomatizmWeiting.ID]
другими корректирующими действиеями
У автоматзма есть свой параметр Energy, но т.к. автоматизм может использоваться в разных случаях,
   лучше для этих конкретных случаев использования уточнять энергичность
   с помощью мозжечковых рефлексов.
Возвращает true, если была оптимизация и нужно запустить такой автоматизм на новое исполнение для проверки.
todo - типа запроса на оптимизацию:
0 - оптимизировать в зависимости от предыдущего значения
1 - усилить
 */
func cerebellumCoordination(atmzm *Automatizm, todo int) bool {
	if atmzm == nil{ return false	}
	if transfer.IsPsychicGameMode {return false} // в игровом режме никаких коррекций, тупая запись нового
	// TODO: учитывать энергосбережение и самосохранение !!!???
	cr := cerebellumReflexFromMotorsID[atmzm.ID]
	if cr != nil { // уже есть мозжечковый рефлекс
		// в прошлый раз было добавлено энергии
		oldEnerg := cr.addEnergy
		if oldEnerg + atmzm.Energy > 9 { // был добавлено лишее, нужно постепенно уменьшить, а не скакать от плюса к минусу
			if todo == 1 { return false }
			// если остро не хватает энергии
			if gomeostas.GomeostazParams[0] < 10.0 { return false }
			cr.addEnergy-- // может быть отрицательное число, чтобы уменьшить энергию автоматизма
		}
		if todo == 1 || oldEnerg + atmzm.Energy < 1 { // был добавлено лишее, нужно постепенно добавлять, а не скакать от плюса к минусу
			cr.addEnergy++
		}
		return true
	} else { // нет мозжечкового рефлекса
		// добавить мозжечковый рефлекс
		_, cr = createNewCerebellumReflex(0, 0, atmzm.ID,true)
		if cr != nil {
				if todo == 1 { return false	}
			// добавить энергичность по максимуму
			cr.addEnergy = 10 - atmzm.Energy // хотя при выполнении автоматизма будет подрезана лишняя энергия
			// atmzm.Belief = 3
if doWritingFile {SaveCerebellumReflex() }
			return true
		}
	}
	return false
}
____________________________________________

BOT/brain/psychic/cerebellum_reflexes.go
____________________________________________
/* Рефлексы мозжечка для корректировки автоматизмов 

Допонение автоматизма другими корректирующими действиеями 
или корректировка самого автоматизма.

 Для размышления:
24	 	Как оператору оценивать результат, когда бот выдает автоматизм с большей энергичностью, если это касается вербального общения? Типа он громче/злее отвечает?
25	 	Если бы это был механический автоматизм типа пары антагонистических движений, в этом был бы смысл как в попытке найти оптимальное корректирующее действие.
26	 	А сейчас это просто бестолковое накручивание энергичности которое никак не оценивается оператором потому, что не понятно, как это оценивать.
27	 	Может вообще побрить эти мозжечковые коррекции как бессмысленные для нашего вербального проекта?
28	 	Или может привязать вместо энергичности изменение тона и контекста сообщения, что будет более логично.
29	 	Тем более, что до сих пор мы никак не использовали их в плане адаптации.
30
31	 	Если вместо увеличения энергичности ставить в порядке возрастания (см. флажки на пульте):
32	 	тон повышенный + контекст нормальный
33	 	тон повышенный + контекст агрессивный
34	 	тон повышенный + контекст защитный
35	 	тон повышенный + контекст протест
36
37	 	И в порядке убывания:
38	 	тон вялый + контекст нормальный
39	 	тон вялый + контекст плохой
40
41	 	Это уже не будет +-10 единиц, но по сути это будет активация дополнительных элементов воздействия на оператора, которые могли в теории образоваться
42	 	как рефлекторная реакция на изменение энергичности автоматизма. То есть изменение энергичности выполнения автоматизма во многих случаях это добавление
43	 	каких то составляющих к нему потому, что не все автоматизмы это мышечные сокращения.
*/

package psychic

import (
	"BOT/brain/transfer"
	"BOT/lib"
	"strconv"
	"strings"
)

/*  по результатам выполнения автоматизма выбираются дополнительные действия
или изменяется сила действия автоматизма.
Это - средство не переписывать автоматизм, а оптимизировать его.
В качестве дополнительных действий используются имеющиеся автоматизмы на основе которых создаются
мозжечковые рефлексы
 */
type cerebellumReflex struct {
	id int
	typeAut int 									// тип корректруемого автоматизма: 0 - это ID моторного, 1 - ID ментального
	sourceAutomatizmID int 				// корректируемый моторный или ментальный автоматизм
	addEnergy int 								// добавление (-убавление) силы действия Energy. Может быть отрицательное число, чтобы уменьшить энергию автоматизма
	// кроме корректирования самого автоматизма по силе действия, могут быть запущены дополнительные автоматизмы:
	additionalAutomatizmID []int 	// массив ID дополнительных моторных автоматизмов
	additionalMentalAutID []int 	// массив ID дополнительных ментальных автоматизмов
}
// общий массив рефлексов мозжечка
var cerebellumReflexFromID = make(map[int]*cerebellumReflex)
// рефлексы мозжечка по моторному ID
var cerebellumReflexFromMotorsID = make(map[int]*cerebellumReflex)
// рефлексы мозжечка по ментальному ID
var cerebellumReflexFromMentalsID = make(map[int]*cerebellumReflex)

func cerebellumReflexInit() {
	loadCerebellumReflex()
}

// последний ID рефлексов мохжечка
var lastCRid = 0

// создать новый автоматизм
// В случае отсуствия автоматизма создается ID такого отсутсвия, пример такой записи: 2|||0|0| - ID=2
func createNewCerebellumReflex(id int, typeAut int, sourceAutomatizmID int,CheckUnicum bool)(int, *cerebellumReflex){
	if CheckUnicum {
		oldID, oldVal := checkUnicumCerebellumReflex(typeAut, sourceAutomatizmID)
		if oldVal != nil { return oldID, oldVal }
	}

	if id == 0 {
		lastCRid++
		id = lastCRid
	} else {
		if lastCRid < id {
			lastCRid = id
		}
	}

	var node cerebellumReflex
	node.id = id
	node.typeAut = typeAut
	node.sourceAutomatizmID = sourceAutomatizmID
	node.addEnergy = 5 // сразу придаст максимум, сложившись с энергией автоматизма :)
	// node.additionalAutomatizmID = additionalAutomatizmID
	cerebellumReflexFromID[id] = &node
	if typeAut == 0 {
		cerebellumReflexFromMotorsID[sourceAutomatizmID] = &node
	} else {
		cerebellumReflexFromMentalsID[sourceAutomatizmID] = &node
	}
	if doWritingFile {SaveCerebellumReflex() }
	return id, &node
}

// поиск рефлекса мозжечка
func checkUnicumCerebellumReflex(typeAut int, sourceAutomatizmID int)(int, *cerebellumReflex) {
	for id, v := range cerebellumReflexFromID {
	if v==nil{continue}
		if typeAut == v.typeAut && sourceAutomatizmID == v.sourceAutomatizmID { return id, v }
	}
	return 0, nil
}

// Сохранить рефлексы мозжечка
// структура записи: id|typeAut|sourceAutomatizmID||addEnergy|additionalAutomatizmID|additionalMentalAutID
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0|
func SaveCerebellumReflex() {
	var out = ""
	for k, v := range cerebellumReflexFromID {
	if v==nil{continue}
		out += strconv.Itoa(k)+"|"
		out += strconv.Itoa(v.typeAut)+"|"
		out += strconv.Itoa(v.sourceAutomatizmID)+"|"
		out += strconv.Itoa(v.addEnergy)+"|"
		for i := 0; i < len(v.additionalAutomatizmID); i++ {
			out += strconv.Itoa(v.additionalAutomatizmID[i]) + ","
		}
		out += "|"
		for i := 0; i < len(v.additionalMentalAutID); i++ {
			out += strconv.Itoa(v.additionalMentalAutID[i]) + ","
		}
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_psy/cerebellum_reflex.txt", out)
}

// Загрузить рефлексы мозжечка
// структура записи: id|typeAut|sourceAutomatizmID||addEnergy|additionalAutomatizmID|additionalMentalAutID
func loadCerebellumReflex() {
	cerebellumReflexFromID = make(map[int]*cerebellumReflex)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/cerebellum_reflex.txt")
	for n := 0; n < len(strArr); n++ {
		p := strings.Split(strArr[n], "|")
		if len(p) < 5 { return }
		id, _ := strconv.Atoi(p[0])
		typeAut, _ := strconv.Atoi(p[1])
		sourceAutomatizmID, _ := strconv.Atoi(p[2])
		addEnergy, _ := strconv.Atoi(p[3])
		a := strings.Split(p[4], "|")
		var additionalAutomatizmID []int
		for i := 0; i < len(a); i++ {
			aid, _ := strconv.Atoi(a[i])
			additionalAutomatizmID = append(additionalAutomatizmID, aid)
		}
		a = strings.Split(p[5], "|")
		var additionalMentalAutID []int
		for i := 0; i < len(a); i++ {
			aid, _ := strconv.Atoi(a[i])
			additionalMentalAutID = append(additionalMentalAutID,aid)
		}
var saveDoWritingFile= doWritingFile; doWritingFile =false
		_, ca := createNewCerebellumReflex(id, typeAut, sourceAutomatizmID,false)
doWritingFile =saveDoWritingFile
		ca.addEnergy = addEnergy
		ca.additionalAutomatizmID = additionalAutomatizmID
		ca.additionalMentalAutID = additionalMentalAutID
	}
	return
}

// вернуть скорректированную силу действия
func getCerebellumReflexAddEnergy(kind int, automatizmID int) int {
	var e *cerebellumReflex

	if transfer.IsPsychicGameMode {return 0} // в игровом режиме не должно быть коррекций, иначе обучающие действия кнопок начнут неадекватно прогрессировать
	if kind == 0 {
		e = cerebellumReflexFromMotorsID[automatizmID]
	} else {
		e = cerebellumReflexFromMentalsID[automatizmID]
	}

	if e == nil { return 0 }
	return e.addEnergy
}

// выполнить дополнительные мозжечковые автоматизмы сразу после выполняющегося автоматизма
var wasRunAutmzmID=0// защелка от бесконечного цикла RumAutomatizm() - runCerebellumAdditionalAutomatizm() - RumAutomatizmID() - RumAutomatizm()
var wasRunMentalAutmzmID=0
func runCerebellumAdditionalAutomatizm(kind int, automatizmID int) {
	if wasRunAutmzmID>0{// только один вызов runCerebellumAdditionalAutomatizm для данного автоматизма
		wasRunAutmzmID=0
		return
	}
	if wasRunMentalAutmzmID>0{// только один вызов runCerebellumAdditionalAutomatizm для данного автоматизма
		wasRunMentalAutmzmID=0
		return
	}
	var cr *cerebellumReflex

	if kind == 0 {
		cr = cerebellumReflexFromMotorsID[automatizmID]
	} else {
		cr = cerebellumReflexFromMentalsID[automatizmID]
	}
	if cr == nil { return	}
	if kind == 0 {
		aArr := cr.additionalAutomatizmID
		for i := 0; i < len(aArr); i++ {
			wasRunAutmzmID=aArr[i]
			RumAutomatizmID(aArr[i])
		}
	} else {
		aArr := cr.additionalMentalAutID
		for i := 0; i < len(aArr); i++ {
// TODO это старый вариант мент.автоматизмов, который нужно просто заменить на моторные автоматизмы, копровождаюзие мозжечковый рефлекс.
			wasRunMentalAutmzmID=aArr[i] //???????
			//RunMentalAutomatizmsID(aArr[i])
		}
	}
}
____________________________________________

BOT/brain/psychic/dominanta.go
____________________________________________
/* Доминанта нерешенной проблемы
Это - атрибут 5-й стадии развития - творчества

ДОМИНАНТА - нерешенная проблема,
т.е. не найденное пока что ответное действие (или цепочка ответных действий) или цепочка мыслительных операций.
Цель - решение проблемы problemTreeID где ставится цель:
в func infoFunc8 ищестя текущая цель problemTreeInfo.purposeID устанавливается в getMentalPurpose()

!!! Активная Доминанта, если она есть, ВСЕ ВРЕМЯ сопровождает появление новой инфы восприятия так,
чтобы была возможность найти решение по аналогии. Модели понимания дают аналогии действий.
Только этим она и отличается от простой мыслительной Темы и Цели.

В структуре доминанты есть самая общая цель типа ProblemTreeNode.purposeID,
а также тема мышления ProblemTreeNode.themeID
которые сохраняются как образ текущей ветки дерева проблем detectedActiveLastProblemNodID

Главным идентификатором Доминанты является detectedActiveLastProblemNodID, НО
под одним detectedActiveLastProblemNodID могут быть создано несколько доминант для разных objectID (которого может и не быть).
Поэтому при выборе Доминанты следует находить ту, что соотвествует текущему значению extremImportanceObject.

Решением доминанты (закрытие гештальта) является нахождение удачного действия типа tryAction

Сохраняется в файле /memory_psy/dominanta.txt
в формате:
ID|problemTreeID|weight|birthTime|endTime|objectID|targetActionID|typeTargetAction|needExecuteTargetAction|tryActionsKey|isSuccess

Доминанты не удаляются и составляют базу личного опыта. TODO - как-то использовать.
Если доминанта решена, то можно использовать такое решение хоть сразу, хоть потом,
но озарение возникает сразу в func checkRelevantAction(

В творчестве доминанта используется в трех ипостасях:
1. нахождение действия по случайной аналогии (это сделано)
и то, что использует стек обобщения saveBaseLinksCicleSynthesis (который не имеет пока поддержки в mental_memories.go):
2. ТВОРЧЕСКОЕ СОПОСТАВЛЕНИЕ И ОБОБЩЕНИЕ
3. ТВОРЧЕСКАЯ ФАНТАЗИЯ
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

// /////////////////////////////////////
type Dominanta struct {
	ID            int
	problemTreeID int // ID дерева проблем - общие ситуация, тема и цель
	// вес значимости проблемы - чисто оценочно, Доминанты не конкурируют по весу значимости
	weight int
	// время рождения проблемы в днях жизни (как в условных рефлексах)
	birthTime int
	// срок актуальности цели, в днях, может быть очень приблизительным или не ограниченным (endTime==0)
	endTime int
	/*объект размышления типа extremImportance Тут может быть новая фраза и т.п. новые дела
	- объект, с которым возможно совершить какое-то действие.
	Это должен быть Стимул, для которого ищется Ответ типа extremImportanceObject
	т.е. с каждым стимулом нужно проверять, не это ли данный объект, с которым возможно попробовать совершить действие
	*/
	objectID int
	/* целевое действие, которые желательно совершить с объектом objectID
	TODO в принципе, тут может быть массив действий или же цепочка действий
	*/
	targetActionID int
	/* тип образа действия:
	0 - изменение состояния жизненных параметров - уточняющихся в образе objectID
	1 - изменение эмоционального состояния
	2 - достижение (или избегание в случае needExecuteTargetAction==0) образа ситуации
	3 - моторное действие типа ActionsImage
	4 - образ понимания сути объекта objectID - реализуется в ходе решения проблемы доминанты
	с получением определенного вида опыта (модели понимания, правила, категории, обобщения и т.п.)
	Решение об удовлетворенности реализацией типа 4 принимается осознанно т.е. через информационную картину, с закрытием проблемы.

	*/
	typeTargetAction int
	//метка о том, нужно ли совершить такое действие (1) или, наоборот, нужно его избегать (0).
	needExecuteTargetAction int // только 0 или 1

	/* действия, которые были опробованы при попытке решения (не только для доминанты).
	индекс для tryActionsID[] действия, которое принято как пробное решение, вначале =-1
	выдать массив *tryAction для ID доминанты
	dAIarr:=getDomTryActionArr(dominantaID int)
	или просто aArr:=tryActionArr[v.ID] т.к. у tryActionArr=make(map[int][]*tryAction)  index == Dominanta.ID
	При открытии доминанты tryActionsKey может быть ==-1 но заполняться ID образов действий в ходе решения доминанты.
	TODO с вводом targetActionID вроде бы нет смысла поддерживать tryActionsKey но в структуре tryAction есть ээффект от попытки,
	тут нужно оптмизировать структуру...
	*/
	tryActionsKey int // для каждой доминанты является ключом tryActionArr=make(map[int][]*tryAction)

	/* // степень решения проблемы:
	0 - ничего нет,
	1 - начато решение, - уже есть найденные tryActionsKey но не точные
	2 - частичное решение, - есть точно подходящий к условиям tryActionsKey
	3 - успешное решение - эффект проверен, гельштат закрыт, можно не думать, а пользолваться опытом
	4 - потеря актуальности доминанты, более не рассматривается
	*/
	isSuccess int
}

// var DominantaProblem=make(map[int]*Dominanta)
var DominantaProblem []*Dominanta // сам массив
// var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteDominantaProblem(index int, value *Dominanta) {
	if index >= len(DominantaProblem) {
		newSlice := make([]*Dominanta, index+1)
		copy(newSlice, DominantaProblem)
		DominantaProblem = newSlice
	}
	DominantaProblem[index] = value
}

// считывание члена
func ReadeDominantaProblem(index int) (*Dominanta, bool) {
	if index >= len(DominantaProblem) || DominantaProblem[index] == nil {
		return nil, false
	}
	return DominantaProblem[index], true
}

///////////////////////////////////////

var CurrentProblemDominanta *Dominanta

////////////////////////////////////////////

////////////////////////////////////////////
/* создать новую доминанту проблемы для detectedActiveLastUnderstandingNodID

 */
var lastDominantaID = 0

func createNewDominanta(id int, problemTreeID int, weight int, endTime int, objectID int, targetActionID int, typeTargetAction int, needExecuteTargetAction int, birthTime int, tryActionsKey int, isSuccess int, CheckUnicum bool) (int, *Dominanta) {
	if CheckUnicum {
		oldID, oldVal := checkUnicumDominanta(problemTreeID, objectID, targetActionID, typeTargetAction, needExecuteTargetAction)
		if oldVal != nil {
			return oldID, oldVal
		}
	}
	if id == 0 {
		lastDominantaID++
		id = lastDominantaID
	} else {
		//		newW.ID=id
		if lastDominantaID < id {
			lastDominantaID = id
		}
	}

	var node Dominanta
	node.ID = id
	node.problemTreeID = problemTreeID
	node.weight = weight
	node.objectID = objectID
	node.targetActionID = targetActionID
	node.typeTargetAction = typeTargetAction
	node.endTime = endTime
	node.needExecuteTargetAction = needExecuteTargetAction
	node.birthTime = birthTime
	node.tryActionsKey = tryActionsKey
	node.isSuccess = isSuccess

	//DominantaProblem[id]=&node
	WriteDominantaProblem(id, &node)

	return id, &node
}

// /////////////
func checkUnicumDominanta(problemTreeID int, objectID int, targetActionID int, typeTargetAction int, needExecuteTargetAction int) (int, *Dominanta) {
	for id, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if problemTreeID != v.problemTreeID || objectID != v.objectID ||
			targetActionID != v.targetActionID ||
			typeTargetAction != v.typeTargetAction ||
			needExecuteTargetAction != v.needExecuteTargetAction {
			continue
		}
		return id, v
	}
	return 0, nil
}

//////////////////////////////////////////////////////

// ///////////////////////////////  if doWritingFile{SaveProblemDominenta() }
func SaveProblemDominenta() {
	saveProblemTryCount()
	saveTryActionArr()

	var out = ""
	for k, v := range DominantaProblem {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		out += strconv.Itoa(v.problemTreeID) + "|"
		out += strconv.Itoa(v.weight) + "|"
		out += strconv.Itoa(v.birthTime) + "|"
		out += strconv.Itoa(v.endTime) + "|"
		out += strconv.Itoa(v.objectID) + "|"
		out += strconv.Itoa(v.targetActionID) + "|"
		out += strconv.Itoa(v.typeTargetAction) + "|"
		out += strconv.Itoa(v.needExecuteTargetAction) + "|"

		out += strconv.Itoa(v.tryActionsKey) + "|"
		out += strconv.Itoa(v.isSuccess)
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/dominanta.txt", out)
}

/////////////////////////////////////////

func loadProblemDominenta() {
	loadProblemTryCount()
	loadTryActionArr()

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/dominanta.txt")
	cunt := len(strArr)
	//	DominantaProblem=make(map[int]*Dominanta)
	DominantaProblem = make([]*Dominanta, cunt) //задать сразу имеющиеся в файле число
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		problemTreeID, _ := strconv.Atoi(p[1])
		weight, _ := strconv.Atoi(p[2])
		birthTime, _ := strconv.Atoi(p[3])
		endTime, _ := strconv.Atoi(p[4])
		objectID, _ := strconv.Atoi(p[5])
		targetActionID, _ := strconv.Atoi(p[6])
		typeTargetAction, _ := strconv.Atoi(p[7])
		needExecuteTargetAction, _ := strconv.Atoi(p[8])
		tryActionsKey, _ := strconv.Atoi(p[9])
		isSuccess, _ := strconv.Atoi(p[10])

		var saveDoWritingFile = doWritingFile
		doWritingFile = false

		createNewDominanta(id, problemTreeID, weight, endTime, objectID, targetActionID, typeTargetAction, needExecuteTargetAction, birthTime, tryActionsKey, isSuccess, false)

		doWritingFile = saveDoWritingFile
	}
	return
}

//////////////////////////////////

// степень решенной Доминанты (зактыть гештальт isSuccess=3 )
func solutionDominanta(dID int, isSuccess int) {
	//	DominantaProblem[dID].isSuccess=isSuccess
	node, ok := ReadeDominantaProblem(dID)
	if !ok {
		return
	}
	node.isSuccess = isSuccess
	clinerProblemDominenta(dID) // удаление массива пробных действий
}

// ///////////////////////////////////////////
// удаление доминанты, проставив isSuccess=true
func clinerProblemDominenta(dID int) {

	// удаление массива пробных действий
	removeTryActionArr(dID)
}

//////////////////////////////////

///////////////////////////////////
/*  Это не нужно!
// наиболее важная доминанта в заданном эмоциональном контексте
func getMainDominanta(emotionID int) int {
	var dominantaID=0
	// TODO

		switch emotionID {
		case 1: //Пищевой	- Пищевое поведение, восполнение энергии, на что тратится время и тормозятся антагонистические стили поведения.

		case 2: //Поиск	- Поисковое поведение, любопытство. Обследование объекта внимания, поиск новых возможностей.

		case 3: //Игра	- Игровое поведение - отработка опыта в облегченных ситуациях или при обучении.

		case 4: //Гон	- Половое поведение. Тормозятся антагонистические стили

		case 5: //Защита	- Оборонительные поведение для явных признаков угрозы или плохом состоянии.

		case 6: //Лень	- Апатия в благополучном или безысходном состоянии.

		case 7: //Ступор	- Оцепенелость при непреодолимой опастbase_context_activnostности или когда нет мотивации при благополучии или отсуствии любых возможностей для активного поведения.

		case 8: //Страх	- Осторожность при признаках опасной ситуации.

		case 9: //Агрессия	- Агрессивное поведение для признаков легкой добычи или защиты (иногда - при плохом состоянии).

		case 10: //Злость	- Безжалостность в случае низкой оценки .

		case 11: //Доброта	- Альтруистическое поведение.

		case 12: //Сон - Состояние сна. Освобождение стрессового состояния. Реконструкция необработанной информации.

		}

return dominantaID
}*/
////////////////////////////////////////////////////

// для пульта
func GetDominantaIDString(id int) string {

	return "Еще не сделан вывод Доминант"
}

////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/dominanta_list_pult.go
____________________________________________
/*   показ доминант ДЛЯ ПУЛЬТА

 */

package psychic

import (
	"BOT/lib"
	"strconv"
)

func GetDominantsListToPult()string{
	var out=""

	for k, v := range DominantaProblem {
		if v==nil{continue}
		out+="ID=<b><span style='cursor:pointer;color:blue' onClick='show_dominant(" + strconv.Itoa(k) + ")'>" + strconv.Itoa(k) + "</span>|"
		out+=" <span title='вес значимости проблемы'>Вес: "+strconv.Itoa(v.weight)+"</span>|"
		out+=" <span title='степень решения проблемы'>Стадия решения: "+strconv.Itoa(v.isSuccess)+"</span>|"
		out+=" <span title='ID rонечного узла активной ветки дерева проблем'>ID дерева проблем: <b><span style='cursor:pointer;color:blue' onClick='get_problem_tree(" + strconv.Itoa(v.problemTreeID) + ")'>" + strconv.Itoa(v.problemTreeID) + "</span>" + "</span>|"
		out+=" <span title='объект размышления типа extremImportance'>Объект проблемы: <b><span style='cursor:pointer;color:blue' onClick='show_object(" + strconv.Itoa(v.objectID) + ")'>" + strconv.Itoa(v.objectID) + "</span>" + "</span>|"
		lib.MapCheck(MapGwardTryActionArr)
		aArr:=tryActionArr[v.tryActionsKey]
		out+=" Попыток решения <b>" + strconv.Itoa(len(aArr))+"<br><br>\n"
	}
	return out
}
////////////////////////////////////////////////////////////



//
func DominantaInfoStr(id int)string{
	var out=""
//	dm:=DominantaProblem[id]
	dm,ok:=ReadeDominantaProblem(id)
	if !ok{
		return "Нет доминанты с ID="+strconv.Itoa(id)
	}

	out+="Доминанта ID="+strconv.Itoa(id)+":<br>"

	out+="вес значимости проблемы: "+strconv.Itoa(dm.weight)+"<br>"
	out+="степень решения проблемы: "+strconv.Itoa(dm.isSuccess)+"<br>"

	out+="Конечный узел активной ветки дерева проблем<b><span style='cursor:pointer;color:blue' onClick='get_problem_tree(" + strconv.Itoa(dm.problemTreeID) + ")'>" + strconv.Itoa(dm.problemTreeID) + "</span>" + "<br>\n"
	out+="объект размышления типа extremImportance <b><span style='cursor:pointer;color:blue' onClick='show_object(" + strconv.Itoa(dm.objectID) + ")'>" + strconv.Itoa(dm.objectID) + "</span>" + "<br>\n"
	lib.MapCheck(MapGwardTryActionArr)
	aArr:=tryActionArr[dm.tryActionsKey]
	out+="число попыток решения <b>" + strconv.Itoa(len(aArr))+" <br>\n"

	return out
}
/////////////////////////////////////////
____________________________________________

BOT/brain/psychic/dominanta_support.go
____________________________________________
/*
Счетчик проблем для открытия доминаниы.
Массив пробных действий доминанты

*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/////////////////////////////////////

/*
Счетчик проблем: если для данного ID дерева проблем problemTryCount[id] >3 -
на пятой стпени разивтия открывать доминанту. Счетчик продолжает накапливаться.
Сохраняется в файле problem_counts.txt
*/
var problemTryCount = make(map[int]int)
var MapGwardProblemTryCount = lib.RegNewMapGuard()

///////////////////////////////////////

func saveProblemTryCount() {
	var out = ""
	lib.MapCheckBlock(MapGwardProblemTryCount)
	for k, v := range problemTryCount {
		out += strconv.Itoa(k) + "|"
		out += strconv.Itoa(v)
		out += "\r\n"
	}
	lib.MapFree(MapGwardProblemTryCount)
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/dominanta_try_count.txt", out)
}

func loadProblemTryCount() {
	lib.MapCheckWrite(MapGwardProblemTryCount)
	problemTryCount = make(map[int]int)
	lib.MapFree(MapGwardProblemTryCount)

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/dominanta_try_count.txt")
	cunt := len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		ID, _ := strconv.Atoi(p[0])
		count, _ := strconv.Atoi(p[1])

		//problemTryCountMu.Lock()
		//defer problemTryCountMu.Unlock()

		lib.MapCheckWrite(MapGwardProblemTryCount)
		problemTryCount[ID] = count
		lib.MapFree(MapGwardProblemTryCount)
	}
}

///////////////////////////////////////

/////////////////////////////////////
/* действия, которые были опробованы, не только для доминанты.
Сохраняются в файле dominanta_try_ctions.txt
в формате dominantaID|[]tryAction 1|[]tryAction 2|....

Очищаются с удалением доминанты с помощью removeTryActionArr(dominantaID int)
в том числе и для временных массивов (отрицательные dominantaID)
*/
type tryAction struct {
	ID             int
	actionsImageID int
	effect         int // отрицательный, нулевой или положительный
}

/*
	массив испробованных действий для данных условий объекта внимания importance

index == Dominanta.ID

Могут быть пробные действия и без доминанты,
тогда индексом в tryActionArr[-id] отрицательное значение ID дерева проблем.
Такие действия не записываются в файл и сохраняются только до перезагрузки (как кратковременная память).
*/
var tryActionArr = make(map[int][]*tryAction)
var MapGwardTryActionArr = lib.RegNewMapGuard()

///////////////////////////////////////

// удаление массива пробных действий
func removeTryActionArr(dominantaID int) {
	lib.MapCheckBlock(MapGwardTryActionArr)
	ad := tryActionArr[dominantaID]
	for k, _ := range ad {
		delete(tryActionArr, k)
	}
	lib.MapFree(MapGwardTryActionArr)
}

/////////////////////////////////

// удаление временных массивов пробных действий (отрицательные значяения индеков) - во сне
func RemoveTemporeryTryActionArr() {
	lib.MapCheckBlock(MapGwardTryActionArr)
	for k, _ := range tryActionArr {
		if k > 0 {
			continue
		}
		delete(tryActionArr, k)
	}
	lib.MapFree(MapGwardTryActionArr)
}

/////////////////////////////////

// // сохраниение tryActionArr в формате dominantaID|[]tryAction 1|[]tryAction 2|....
// полный вариант: dominantaID|id,actionsImageID,effect|id2,actionsImageID2,effect2|....
func saveTryActionArr() {
	var out = ""
	lib.MapCheckBlock(MapGwardTryActionArr)
	for k, v := range tryActionArr {
		if k < 0 { // такие действия хранятся временно
			continue
		}
		out += strconv.Itoa(k) + "|"
		for n := 0; n < len(v); n++ {
			out += strconv.Itoa(v[n].ID) + ","
			out += strconv.Itoa(v[n].actionsImageID) + ","
			out += strconv.Itoa(v[n].effect) // в конце нельзя ставить / иначе в loadTryActionArr будет определять пустую строку и вылетать
		}
		out += "\r\n"
	}
	lib.MapFree(MapGwardTryActionArr)
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/dominanta_try_actions.txt", out)
}

func loadTryActionArr() {

	tryActionArr = make(map[int][]*tryAction)

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/dominanta_try_actions.txt")
	cunt := len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		dominantaID, _ := strconv.Atoi(p[0])
		// все записанные tryAction:
		for m := 1; m < len(p); m++ {
			a := strings.Split(p[m], ",")
			nodeID, _ := strconv.Atoi(a[0])
			nodeActionsImageID, _ := strconv.Atoi(a[1])
			nodeEffect, _ := strconv.Atoi(a[2])
			addNewTryAction(nodeID, dominantaID, nodeActionsImageID, nodeEffect, false)
		}
	}
}

//////////////////////////////////////////////////////

/*
	добавить новое действие actionsImageID для доминанты или временное в массив действий, которые были опробованы

Для одной dominantaID м.б. массив []*tryAction
Возвращает ID новой tryAction и сам *tryAction
*/
var tryActionLastID = 0

func addNewTryAction(id int, dominantaID int, actionsImageID int, effect int, CheckUnicum bool) (int, *tryAction) {
	if CheckUnicum {
		oldID, oldVal := checkTryAction(dominantaID, actionsImageID)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		tryActionLastID++
		id = tryActionLastID
	} else {
		//		newW.ID=id
		if tryActionLastID < id {
			tryActionLastID = id
		}
	}
	var node tryAction
	node.ID = id
	node.actionsImageID = actionsImageID
	node.effect = effect

	lib.MapCheckWrite(MapGwardTryActionArr)
	tryActionArr[dominantaID] = append(tryActionArr[dominantaID], &node)
	lib.MapFree(MapGwardTryActionArr)

	return id, &node
}

////////////////////////

// выдать *tryAction по actionsImageID
func checkTryAction(dominantaID int, actionsImageID int) (int, *tryAction) {
	lib.MapCheckBlock(MapGwardTryActionArr)
	for id, v := range tryActionArr {
		for n := 0; n < len(v); n++ {
			if v[n].actionsImageID == actionsImageID {
				lib.MapFree(MapGwardTryActionArr)
				return id, v[n]
			}
		}
	}
	lib.MapFree(MapGwardTryActionArr)
	return 0, nil
}

/////////////////////////////////////////

// выдать массив *tryAction для ID доминанты
func getDomTryActionArr(dominantaID int) []*tryAction {
	// т.к. у tryActionArr=make(map[int][]*tryAction)  index == Dominanta.ID
	return tryActionArr[dominantaID]
}

// //////////////////////////////////////
// выдать массив []int c tryAction.ID для ID доминанты
func getDomTryActionIDArr(dominantaID int) []int {
	var arr []int
	lib.MapCheckBlock(MapGwardTryActionArr)
	for id, v := range tryActionArr {
		if id == dominantaID {
			for n := 0; n < len(v); n++ {
				arr = append(arr, v[n].ID)
			}
		}
	}
	lib.MapFree(MapGwardTryActionArr)
	return arr
}

////////////////////////////////////////

/*
	выявления завершения актуальности доминанты при сканировании массива доминант

Это - функция, обратная открытию доминанты - закрытие уже не актуальной доминанты.
В первую очередь учитывается endTime
*/
func lookForUnactualDominant() {
	idleness := isIdleness()
	for dID, v := range DominantaProblem {
		if v == nil {
			continue
		}

		if v.isSuccess == 3 || v.isSuccess == 4 { // не смотреть закрытые проблемы
			continue
		}
		if v.endTime != 0 {
			diffT := LifeTime - v.birthTime
			if v.endTime > diffT { // уже не актуальна
				// закрыть доминанту
				v.isSuccess = 4
				clinerProblemDominenta(dID)
			}
		}

		// TODO другие критерии потери актуальности

		// необходимо время от времени возобновлять в памяти, напоминая зачем они были поставлены.
		if idleness { //Если процесс в лени, а не во сне
			// TODO выйти из лени и начать обрабатывать доминанту - ее экстремальный объект
		}

	}

}

//////////////////////////////////////////////////////////////////////////

/*
При появлении экстремального стимула на 3-м уровне осмысления определить, нет ли доминанты с Dominanta.objectID.
Если есть, то попробовать решить доминанту реализацией ее действия Dominanta.typeTargetAction.
Если идентифицирован Dominanta.objectID возвращает true
*/
func checkDominantsJbject(objectID *extremImportance) bool {
	if objectID == nil {
		return false
	}
	// при опасной ситуцации не смотреть
	if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {
		return false
	}

	// TODO

	return false
}

////////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/dominanta_working.go
____________________________________________
/*
Работа с Доминантой


Неудовлетворенность существующим реализуется как в виде активности темы ID=9,
так и использованием уже закрытых и открытых доминант для продолжения поиска ассоциаций.
*/

package psychic

import (
	"BOT/lib"
	"strconv"
)

////////////////////////////////////////////
/* создать доминанту
Без extremImportanceObject не создается
*/
func createDominanta(c *cycleInfo) {
	if c.ID == 0 {
		return
	}
	if EvolushnStage < 5 {
		return
	}
	if extremImportanceObject == nil {
		return
	}
	weight := 0 // насколько важна проблема - чисто оценочно, Доминанты не конкурируют по весу значимости
	weight = extremImportanceObject.extremVal

	st := getCurSituation()
	if st != nil {
		switch st.SituationType {
		case 5:
			weight += 1 //оператор не прореагировал на действия в течение периода ожидания - игнорирует
		case 12:
			weight += 1 //Плохое
		case 15:
			weight += 2 //Агрессивное
		case 16:
			weight += 1 //Защитное
			//оператор нажал кнопку
		case 21:
			weight += 1 //Непонятно
		case 23:
			weight += 3 //Наказать
		case 29:
			weight += 1 //Игнорировать
		case 30:
			weight += 3 //Сделать больно
		}
	}
	tm := getCurTheme()
	if tm != nil {
		switch tm.Type {
		case 1:
			weight += 2 //"Негативный эффект моторного автоматизма"
		case 2:
			weight += 1 //"Негативный эффект ментального автоматизма"
		case 3:
			weight += 2 //"Состояние Плохо"
		case 7:
			weight += 1 //"Игнорирование оператором"
		case 9:
			weight += 2 //"Неудовлетворенность существущим"
		case 10:
			weight += 1 //"Непонимание"
		case 14:
			weight += 2 //"Страх"
		}
	}

	birthTime := int(LifeTime / (3600 * 24)) // время рождения
	endTime := 0                             // TODO нужно определить, но время актуальности м.б. не опредеелно ==0

	// Определить целевое действие, которые желательно совершить с объектом objectID:
	//Answer int // 1 - субъективное предположение
	actArr := []int{}    //  TODO нужно определить
	зhraseArr := []int{} //  TODO нужно определить
	toneID := 0          //  TODO нужно определить
	moodID := 0          //  TODO нужно определить
	targetActionID, _ := CreateNewlastActionsImageID(0, 1, actArr, зhraseArr, toneID, moodID, true)

	typeTargetAction := 3 // TODO нужно определить

	needExecuteTargetAction := 1 //TODO: совершить действие targetActionID, 0- избегать действия targetActionID

	id, _ := createNewDominanta(0, detectedActiveLastProblemNodID,
		weight,
		endTime,
		extremImportanceObject.objID,
		targetActionID,
		typeTargetAction,
		needExecuteTargetAction,
		birthTime,
		-1,
		0, true)

	c.log += "Активирована Доминанта id=<b><span style='cursor:pointer;color:blue' onClick='show_dominant(" + strconv.Itoa(id) + ")'>" + strconv.Itoa(id) + "</span><br>"

	//перезапуск осмысления
	//reloadConsciousness(c,0)
	return
}

////////////////////////////////////////////

// выбрать подходящую Доминанту, с присвоением
func getCurrentDominant() {
	if EvolushnStage < 5 || extremImportanceObject == nil {
		/* Если нет экстремального объекта, то все равно выбрать доминанту - уже решенную
		   		для реализации неудовлетворенности существующим
		   использованием уже закрытых или открытых доминант для продолжения поиска ассоциаций
		*/
		var permanentDissatisfaction = false // true - особь с постоянным зудом неудовлетворенности - TODO может изменяться от обстоятельств
		if permanentDissatisfaction {
			getClosedCurrentDominant()
			return
		}
		tm := getCurTheme()
		if tm.Type == 9 { //"Неудовлетворенность существущим"
			getClosedCurrentDominant()
		}
		return
	}

	for _, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if v.isSuccess == 3 || v.isSuccess == 4 { // не смотреть закрытые проблемы
			continue
		}
		objID := extremImportanceObject.objID

		if v.problemTreeID == detectedActiveLastProblemNodID && v.objectID == objID {
			CurrentProblemDominanta = v
			//extremImportanceObject.extremObjID=v.objectID  уже итак есть т.к. доминанта ищется по уже выявленному extremImportanceObject
			CurrentInformationEnvironment.DominantaID = v.ID

			return
		}
	}

	CurrentProblemDominanta = nil
}

// подобрать любую наиболее подходящую к условиям доминанту
func getClosedCurrentDominant() {
	for _, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if v.problemTreeID == detectedActiveLastProblemNodID {
			CurrentProblemDominanta = v
			CurrentInformationEnvironment.DominantaID = v.ID
			return
		}
	}

	return
}

/////////////////////////////////////////////

/*
	Проверка, насколько данный actionsImageID может быть решением для CurrentProblemDominanta.

Если actionsImageID привел к успеху, то он может считатья решением Доминанты.

А если условия != detectedActiveLastProblemNodID , то он может быть принят как текущий curTryActionsID
и т.о. предположительное решение может быть открыто (эавристика)
по аналогии с любым наблюдаемым или совершаемым actionsImageID
и возникает "озарение" с последующей проверкой и запуском пробного действия.

Аргумент successAImgID - только успешные или авторитарно уверенные actionsImageID.
stimulID - это ID объект типа ActionsImage - действие с Пульта

Возвращает:
0 - нет подходящего,
1 - доминанта решена и завершена, можно использовать такое решение хоть сразу, хоть потом
>1 до 3 включительно - найдено пробное решение для проверок, возвращаемое число увеличивается со степенью непохожести условий.

Выбирает первую подходящую как аналог доминанту.
Из-за перебора доминант для сравненеия м.б. тяжелым процессом...
*/
func checkRelevantAction(stimulID int, successAImgID int, effect int) int {
	if EvolushnStage < 5 {
		return 0
	}
	// найти нерешенную диминанту, достаточно подходящую для данного successAImgID
	dominanta := getRelevantDominanta(stimulID)
	if dominanta == nil {
		return 0
	}

	objID := dominanta.objectID

	// прям полностью совпадают условия.
	if detectedActiveLastProblemNodID == dominanta.problemTreeID &&
		dominanta.objectID == objID {

		// является стимул stimulID решающим действием

		/* TODO переделать все с учетом
		Dominanta.objectID
		Dominanta.targetActionID
		Dominanta.typeTargetAction
		Dominanta.needExecuteTargetAction
		*/

		// доминанта решена
		writeDomTryActionArr(successAImgID, effect, objID, dominanta)
		// Доминанта решена, завершить ее и эвристически озариться.

		//Доминанта закрывается только когда после выполнения действий будет получен позитивный эффект
		dominanta.isSuccess = 2
		toConsciousHeuristics(1, successAImgID, dominanta)
		return 1
	}
	////////////////////////////////////////////

	//pT:=ProblemTreeNodeFromID[dominanta.problemTreeID]
	pT, ok := ReadeProblemTreeNodeFromID(dominanta.problemTreeID)
	if !ok {
		return 0
	}

	// совпадают detectedActiveLastNodID и detectedActiveLastUnderstandingNodID
	if detectedActiveLastNodID == pT.autTreeID && detectedActiveLastUnderstandingNodID == pT.situationTreeID &&
		dominanta.objectID == objID { // принять по ассоциации
		writeDomTryActionArr(successAImgID, effect, objID, dominanta)
		// Доминанта решена, завершить ее и эвристически озариться.
		dominanta.isSuccess = 1
		toConsciousHeuristics(2, successAImgID, dominanta)
		return 2
	}
	///////////////////////////////////////////

	// совпадают detectedActiveLastNodID
	if detectedActiveLastNodID == pT.autTreeID &&
		dominanta.objectID == objID { // принять по ассоциации
		writeDomTryActionArr(successAImgID, effect, objID, dominanta)
		// Доминанта решена, завершить ее и эвристически озариться.
		dominanta.isSuccess = 1
		toConsciousHeuristics(3, successAImgID, dominanta)
		return 3
	}
	///////////////////////////////////////////

	return 0
}

// ////////////////
func getRelevantDominanta(stimulID int) *Dominanta {

	//aiObj:=ActionsImageArr[stimulID]
	aiObj, ok := ReadeActionsImageArr(stimulID)
	if ok {
		return nil
	}
	for _, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if v.isSuccess == 3 || v.isSuccess == 4 { // смотреть только НЕ закрытые доминанты
			continue
		}
		//if v.problemTreeID == detectedActiveLastProblemNodID{ НЕТ, ИНАЧЕ БУДЕТ НЕ АНАЛОГИЯ, А ПОЛНОЕ СОВПАДЕНИЕ
		if isAnalogAction(v, stimulID, aiObj) != nil {
			return v
		}
		//		}
	}

	return nil
}

// если подходит акция по аналогии, то вернуть такую доминанту
func isAnalogAction(v *Dominanta, stimulID int, aiObj *ActionsImage) *Dominanta {
	extrObj := getExtremObjFromID(v.objectID)
	if extrObj == nil {
		return nil
	}

	if stimulID == v.objectID {
		return v
	}
	return nil
}

// ///////////////////////
func writeDomTryActionArr(successAImgID int, effect int, objID int, dominanta *Dominanta) {
	dAiIDarr := getDomTryActionIDArr(dominanta.ID) //массив *tryAction для ID доминанты
	ok, tryActionsKey := lib.IndexValInArr(dAiIDarr, objID)
	if !ok { //  дополнить
		tryActionsKey, _ = addNewTryAction(0, dominanta.ID, successAImgID, effect, true)
	}
	// Доминанта предварительно решена, нужно эвристически озариться.
	dominanta.tryActionsKey = tryActionsKey
}

//////////////////////////////////////////////

//////////////////////////////////////////////
/* Эвристически озариться найденным решением
success - 1 - доминанта решена, 2 - не точно, 3 - еще менее точно
*/
func toConsciousHeuristics(success int, successAImgID int, dominanta *Dominanta) {
	if EvolushnStage < 5 {
		return
	}
	CurrentProblemDominanta = dominanta
	mentalInfoStruct.DominantaID = dominanta.ID
	mentalInfoStruct.DominantSuccessAImgID = successAImgID
	mentalInfoStruct.DominantSuccessValue = success
	cycle := createNewCycleIteration() // начать новый цикл
	// т.к. уже не первый шаг, получить текущую Цель РАЗ ОЗАРИЛО, ТО _ ЦЕЛЬ БЫЛА И РЕШЕНА!
	//	infoFunc8(cycle) // Ментальное определение ближайшей Цели mentalInfoStruct.ThemeImageType и актиуивровать дерево проблем

	//	infoFunc19(cycle)//
	//	setAsMaimCycle(cycle.ID)//	сделать цикл главным
	insight(cycle)
	// и начать осмысление

}

////////////////////////////////////////

/*
	запускать решение закрытой доминанты (т.е. взять опыт из ранее решенных проблм) в подходящих условиях.

# ПРи вызове уже учтена стадия развития и отсуствие автоматизма

exactly==false - смотреть с isSuccess>1, exactly==true - только закрытые, точные доминанты

если подходящий автоматизм найден в успешной Доминанте то он будет запущен
*/
func runDominantaAction(c *cycleInfo, exactly bool) bool {
	if extremImportanceObject == nil {
		return false
	}
	for _, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if exactly {
			if v.isSuccess != 3 { // смотреть только успешно закрытые доминанты
				continue
			}
		} else {
			if v.isSuccess < 2 { // смотреть только успешно закрытые доминанты
				continue
			}
		}

		objID := extremImportanceObject.objID

		if v.problemTreeID == detectedActiveLastProblemNodID && v.objectID == objID {
			dominanta := v
			// сделать мот.автоматизм и запустить его
			var actionsImage *ActionsImage // шаблон образа действия

			extrObj := getExtremObjFromID(dominanta.objectID)
			if extrObj == nil {
				return false
			}

			//	ai:=ActionsImageArr[extrObj.extremObjID]
			ai, ok := ReadeActionsImageArr(extrObj.objID)
			if ok {
				return false
			}
			_, actionsImage = CreateNewlastActionsImageID(0, 1, ai.ActID, ai.PhraseID, 0, 0, true)

			if actionsImage != nil {
				mentalInfoStruct.motorAtmzmID = actionsImage.ID
				c.log += "Подходящий автоматизм найден в успешной Доминанте и запущен.<br>"
				infoFunc17(c) // запустить автоматизм и завершить цикл осмысления
				return true
			}
		}
	}

	return false
}

//////////////////////////////////////////

// ///////////////////////////////////////
// закрыть доминанту, котора точно подходит
func checkGestalt(effect int) {
	// curStimulImageID - образ действий оператора перед Ответом
	if curStimulImageID == 0 {
		return
	}
	if curStimulImage.ActID == nil && curStimulImage.PhraseID == nil { // не учитывать с пустым Стимулом
		return
	}
	answerID := LastAutomatizmWeiting.ActionsImageID // ответный образ действий Beast
	if answerID == 0 {
		return
	}
	_, ok := ReadeActionsImageArr(answerID)
	if ok {
		return
	}
	if curStimulImageID == answerID {
		return
	}
	// поиск доминант
	for _, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if v.isSuccess == 3 || v.isSuccess == 4 { // смотреть только НЕ закрытые доминанты
			continue
		}
		if v.problemTreeID != detectedActiveLastProblemNodID {
			continue
		}
		lib.MapCheck(MapGwardTryActionArr)
		aArr := tryActionArr[v.ID]
		// точное соотвествие доминанты:
		if v.objectID == curStimulImage.ID &&
			aArr[v.tryActionsKey].ID == answerID &&
			v.isSuccess == 2 {
			v.isSuccess = 3 // закрыть доминанту
			if v.weight < effect {
				v.weight = effect
			}
			return
		}
	}

}

/////////////////////////////////////////

____________________________________________

BOT/brain/psychic/dreaming.go
____________________________________________
/* ПАССИВНЫЙ (бесцельный) РЕЖИМ РАЗМЫШЛЕНИЯ https://scorcher.ru/thems/29/Passivniy_rezhim_myshleniya.htm
В отличие от целевого мышления о проблеме, пассивное мышление не имеет определенной цели,
но оно всегда начинается с выбора кадра эпиз.памяти (опорного кадра) с которого начинается такое мышление.
В результате прогностического прохода по эпиз.памяти возникают новые опорные кадры для следующего цикла.
При этом правила из кадров могут изменяться так, что возникают новые цепочки действий с их прогнозами.
Приоритет выбора опорных кадров задается в func GotoDreaming.
В первую очередь это - объективный стимул экстремального значения.
Затем субъективно найденный стимул экстремального значения из прошлого. и т.д.

При любом выборе опорного кадра мышление развивается одним алгоритмом - func gotoPassiveMaind(memFr *EpisodicTreeNode)

Обработка актуальных значимостей в режиме свободном от стимулов - пассивный режим,
активный в состоянии, когда Beast не занято выполнением какой-либо задачи, связанной со стимулами вопринятия,
бездействует, грезит наяву или образатывает недоосмысленное.
При актуальных доминантах в этом режиме
фантазируются попытки решения, рассуждения об этом, поиски методов по аналогии с моделями понимания и т.п.

Так же режим активируется при пассивном наблюдении за внешними объектами
- стимулами, не требующими немедленного реагирования.
Стимул, не имеющий модели понимания, вызывает беспокойство и активирует пассивный режим
чтобы исследовать признаки образа, найти ассоциации к ним в личном опыте.
В принципе такой анализ должен проводиться при каждом стимуле, не только при IsDream и откладывать в кеш осмысление нового.

В случае удачного поиска по моделям понимания и наблюдения за объектами (камень ловко катится)
формируется правило, привязанное к данной модели понимания,
которые могут быть использованы для пробных действий и формирование правил в этиз.памяти.
Правила, привязываемые к объектам моделей понимания, формируются при отзеркаливаним действий объекта и эффекта.

При появлении нового стимула режим дорабатывает текущую запущенную функцию и завершается,
оставляя инфо-картину прерванных мыслей для последующей активации пассивного режима.
Но если проблема решается на 1 или 2-м уровне, то режим не прерывается.

Таким образом, режим возникает на 3-м уровне осознания стимула.
Если после данного стимула не находится тема для размышления, следующий может ее дать.
Если нет стимулов более 30 пульсов и нет пассивного размышления, то запустить новый поиск темы.

ПАССИВНЫЙ РЕЖИМ РАЗМЫШЛЕНИЯ имеет метку в цикле cycle.dreaming
так что отдельные циклы могут продолжать пребывать не в режиме размышления (тут сложно - нужно бы решить более универсально...)
Стимул может начать новый главный цикл, но не закрывать режим размышления в уже имеющихся циклах,
который перрывается чтобы выделить ресурсы на текущую проблему, и восстанавливается после ее решения.

Во сне работают оставшиеся циклы дремы и могут активироваться новые циклы пассивного размышления.

func IsDreamMainProcess()// в главном цикле - режим пассивного размышления

ЗАДЕЙСТВАННЫЕ ИНФО-ФУНКЦИИ:
infoFunc10(cycle) - вспомнить самое значимое, что было для начала размышения
func infoFunc18 - Чем именно грозит наиболее значимое из параметров CurrentInformationEnvironment
func infoFunc5 - цикл мышления об экстремально важном объекте extremImportanceObject
func infoFunc20 - Запустить процесс осмысления НАЗАД эпиз.памяти infoFunc5()
func infoFunc202 - Запустить процесс осмысления ВПЕРЕД эпиз.памяти infoFunc5()


В пассивном мышлении могут находится альтернативные правила, которые надо бы запоминать (иначе нафиг нужно такое мышление).
То, что память модифицируется новым опытом - доказано.

Алгоритм описан в комментах над func GotoDreaming(cycle *cycleInfo)
*/

package psychic

import (
	"BOT/lib"
	"strconv"
)

// //////////////////////////////////////////////////////////////////
// время ожидания до активации пассивного режима размышлнения
var waitingTimeForDreamRejim = 10 // может думать в период ожидания ответа

var isDreamInterrupt = false // прерывание процесса размышления с новой актуальной проблемой после стимула. Снимается после решения проблемы или перевода в доминанту.

///////////////////////// ПОддержка шагов итерации пассивного мышления
/* Изменением инфо-картины при шаге итерации Пассивного режима
происходит только в главном цикле и в сновидениях
*/
type passiveEnvironment struct {
	mood          int // сила ощущаемого настроения: плохо -10...0...10 хорошо
	emotionID     int
	problemNodID  int
	danger        bool
	stimulID      int // акция оператора типа ActionsImage
	answerID      int // акция Beast типа ActionsImage
	extremID      int // новый объект для следующей итерации может быть как stimulID, так и answerID
	effect        int // значимость: -10...0...10
	episodFrameID int // кадр эпизода, на котором закончилась итерация
}

// текущее значение после шага пассвного осмысления
var newPassiveEnvironment passiveEnvironment

// обновление инфо-картины при шаге пассивного осмысления
func updateInformationEnvironment() {
	CurrentInformationEnvironment.PsyMood = newPassiveEnvironment.mood
	CurrentInformationEnvironment.PsyEmotionId = newPassiveEnvironment.emotionID
	CurrentInformationEnvironment.danger = newPassiveEnvironment.danger
	CurrentInformationEnvironment.ActionsImageID = newPassiveEnvironment.stimulID
	CurrentInformationEnvironment.AnswerImageID = newPassiveEnvironment.answerID
	CurrentInformationEnvironment.ActualEpisodicMemoryID = newPassiveEnvironment.episodFrameID
	CurrentInformationEnvironment.ExtremImportanceObjectID = newPassiveEnvironment.extremID
}

/*
	метка исчерпывания прохода по цепочкам значимости от начального эпизода

Определяется в
*/
var endOfPassiveIteratuins = true

/////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/*
	начать процесс ПАССИВНЫЙ РЕЖИМ РАЗМЫШЛЕНИЯ
и новые итерации после прохода предыдущих, которые запускаются текущей активной функцией.
Тут приоритетная последовательность видов нахождения объектоа для размышления.

АЛГОРИТМ
С функции func GotoDreaming(cycle *cycleInfo)
 начинается цикл пассивного режима. В конце фоункции, если при обработке было найдено продолжение, то снова вызывается func GotoDreaming. Это - цикл в цикле cycle *cycleInfo.
1. При первом проходе выявляются наиболее значимые объекты внимания и по ним находятся наиболее значащий один кадр эпиз.памяти из всех найденных.
2. По этому кадру начинается шаг обработки func gotoPassiveMaind(cycle *cycleInfo, memFr *EpisodicTreeNode).
В func getNextHistoryEpisodicFrameArr находятся следующие после заданного кадры эпизодов в исторической цепочке до прерывания (пустой кадр) БЕЗ УСЛОВИЙ кроме настроения, что и дает диапазон фантазирования.
после этого шага имеем исходный кадр frm0 памяти и полученный эфктремальный frm2.
4. каждый шаг завершается обработкой с записью новой информации в общую инфокартину (func updateInformationEnvironment).
При проходе, если исходным кадром был с неопределенной значимостью стимула, то эта значимость изменяется найденными, усредняясь.
Если это было нахождение наиболее экстремального продолжения цепочки, и найдена высокая значимость (> insiteCompare = 5), процесс пассивного мышления прерываниеся инсайтом с переходом цикла в главный и его осознанием. Если это случилось во время сновидения - пробуждение.
В обоих случаях записываются два кадра эпиз.памяти подряд (frm0 и frm2) - как результат осознания инсайта.

*/
var curCondintions []int                    // совокупность условий для поиска по эпизодам для findEpisodicBrangeFromObject
var needOptimisationFrame *EpisodicTreeNode // нужно переписать в этом кадре новую значимость для стимула
var needOptimisationFrameCount = 0

func GotoDreaming(cycle *cycleInfo) {
	if isDreamInterrupt {
		return
	}

	cycle.dreaming = true
	if IsSleepingDream {
		cycle.log += "Сновидение пассивного размышления.<br>"
	} else {
		cycle.log += "Состояние пассивного размышления.<br>"
	}

	var curActionImgID = 0                    // найти объект для начала прохода
	var curEpisodicTreeNode *EpisodicTreeNode // этот эпизод нужно оптимизировать после прохода
	if endOfPassiveIteratuins {               // еще не было проходов найти объект для начала пассивного мышления
		needOptimisationFrame = nil
		needOptimisationFrameCount = 0
		curCondintions = []int{ // сначала текущие условия, потом они могут загруляться и изменяться
			CurrentCommonBadNormalWell,
			CurrentEmotionReception.ID,
			detectedActiveLastProblemNodID,
			curActionImgID}

		if cycle.isMainCycle && !IsSleepingDream { // только для главного цикла
			isRepressionStimulsNoise = true //Подавление мешающих стимулов при серьезном поиске решений проблемы.
		}
		//В первую очередь - объективный стимул экстремального значения
		if extremImportanceObject != nil {
			curActionImgID = extremImportanceObject.objID
		}
		if curActionImgID == 0 {
			///выбрать наиболее значимое из еще не угасших циклов кроме главного
			objectsFromPhoneCyclesArr()                                                   // TODO
			if objectsForPassiveToughtArr != nil && len(objectsForPassiveToughtArr) > 0 { // мыслить об этом
				curActionImgID = objectsForPassiveToughtArr[0]
				objectsForPassiveToughtArr = objectsForPassiveToughtArr[0:] // удалить использованный

				if IsSleepingDream {
					resetMineCycleAndBeginAsNew() // погачить главный цикл и начать новый

				} else {
					if !cycle.isMainCycle {
						endBaseIdCycle(cycle.ID) // погасить этот цикл
					}
				}
			}
		}
		if curActionImgID == 0 {
			//Стимул, не имеющий модели понимания, вызывает беспокойство
			if CurrentInformationEnvironment.IsUnknownActionsImageID > 0 {
				curActionImgID = CurrentInformationEnvironment.IsUnknownActionsImageID
			}
		}
		if curActionImgID == 0 {
			/* при опасной ситуации в первую очередь думаем про ответное действие,
			по статусу состояния отсуствия автоматизма: automatizmStatus
			0 - сброс рассматривания автоматизма
			1 - если автоматизм заблокирован
			2 - нет автоматизма или есть старый автоматизм, например, при игнорировании Ответа оператором
			*/
			if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger { // опасно
				if automatizmStatus > 0 && mentalInfoStruct.motorAtmzmID > 0 {
					curActionImgID = mentalInfoStruct.motorAtmzmID
				}
			}

		}
		if curActionImgID == 0 {
			// думаем про доминанту
			if EvolushnStage > 4 {
				// TODO
			}
		}
		if curActionImgID == 0 {
			/*находим недооцененные по значимости стимула неучительские кадры, т.е. с PARAMS[2]==0
			первые 3 объекта с конца, они заполнятся в эпиз.памяти и не будут здесь возникать
			*/
			curEpisodicTreeNode = nil
			unknownObjectsFromMemoryArr()
			if unknownObjectsFrameArr != nil && len(unknownObjectsFrameArr) > 0 { // мыслить об этом
				curActionImgID = unknownObjectsFrameArr[0].Trigger
				curEpisodicTreeNode = unknownObjectsFrameArr[0]     // этот эпизод нужно оптимизировать после прохода
				unknownObjectsFrameArr = unknownObjectsFrameArr[0:] // удалить использованный
			}
		}
	}
	////////////////////////////////////
	if endOfPassiveIteratuins {
		if curActionImgID > 0 { // начать проход пассивного мышления
			// задать условия поиска эпизода
			curCondintions = []int{
				CurrentCommonBadNormalWell,
				CurrentEmotionReception.ID,
				detectedActiveLastProblemNodID,
				curActionImgID}
		}
	} else {
		// задать условия поиска эпизода из newPassiveEnvironment, могут изменять только в сторону упрощений!
		curCondintions = []int{
			newPassiveEnvironment.mood,
			newPassiveEnvironment.emotionID,
			newPassiveEnvironment.problemNodID,
			newPassiveEnvironment.extremID}
	}

	if curActionImgID == 0 { // ничего нет для пассивного мышления
		if cycle.isMainCycle {
			if !IsSleepingDream {
				isRepressionStimulsNoise = false // умолчательное состояние Подавления мешающих стимулов
			}
			if IsSleepingDream {
				id := cycle.ID
				endBaseIdCycle(cycle.ID) // погасить этот цикл
				// найти новый главный цикл по значимости, кроме удаляемого
				foundMainCycle(id)
			}
		} else {
			endBaseIdCycle(cycle.ID) // погасить этот фоновый цикл
		}
		//  закончить пассивное мышление в этом цикле
		return
	}
	/////////////////////////////////////

	if curEpisodicTreeNode == nil { // м.б. определен и тогда не нужно findEpisodicBrangeFromObject
		/* найти самый экстремальный опорный кадр эп.памяти со стимулом extremImportanceObject
		прямыми правилами (а не учительскими)
		*/
		curEpisodicTreeNode = findEpisodicBrangeFromObject(curCondintions)
	} else {
		needOptimisationFrame = curEpisodicTreeNode // нужно переписать в этом кадре новую значимость для стимула
	}

	///////////////////////  эпизод начала мышления выбран
	if curEpisodicTreeNode != nil { // т.к. не смотрим учительские правила, то может не быть найдено

		// с curEpisodicTreeNode кадра жпиз.памяти НАЧИНАЕМ ИТЕРАЦИЮ ПАССИВНОГО МЫШЛЕНИЯ
		endOfPassiveIteratuins = gotoPassiveMaind(cycle, curEpisodicTreeNode) // один шаг обработки

		if needOptimisationFrame != nil { // нужно переписать в этом кадре новую значимость для стимула
			if newPassiveEnvironment.effect != 0 {
				// но условия могут отличаться! нужно усреднять эффект
				needOptimisationFrameCount++
				needOptimisationFrame.PARAMS[2] = int((needOptimisationFrame.PARAMS[2]*(needOptimisationFrameCount-1) + newPassiveEnvironment.effect) / needOptimisationFrameCount)
			}
		}

		// осознание результата, дающее контекст инфо-картины
		if cycle.isMainCycle { // только для главного цикла
			updateInformationEnvironment()
		}

		// исчерпание цепочки или ИНСАЙТ
		var insiteCompare = 5
		if endOfPassiveIteratuins || lib.Abs(newPassiveEnvironment.effect) > insiteCompare {
			// иссякла цепочка образов, конец пассивного мышления высокий эффект, требующий инсайта
			// если найдено что-то важное, то - инсайт (просыпание после сновидения).
			if lib.Abs(newPassiveEnvironment.effect) > insiteCompare {
				// ЭТО ИНСАЙТ
				setAsMaimCycle(cycle.ID) //	сделать цикл главным
				if IsSleeping {
					wakingUp() // разбудить
				}
				/*зписать пару кадров подряд в эпиз.память saveBeginEpisodicTreeNode + curEpisodicTreeNode
				получилась фантастическая цепочка
				*/
				if true {
					saveNewEpisodic(saveBeginEpisodicTreeNode.Trigger, saveBeginEpisodicTreeNode.Action, saveBeginEpisodicTreeNode.PARAMS[0], saveBeginEpisodicTreeNode.PARAMS[2])
					saveNewEpisodic(curEpisodicTreeNode.Trigger, curEpisodicTreeNode.Action, curEpisodicTreeNode.PARAMS[0], curEpisodicTreeNode.PARAMS[2])
				}
			}

			if !IsSleepingDream {
				isRepressionStimulsNoise = false // умолчательное состояние Подавления мешающих стимулов
			}
			return
		}
		GotoDreaming(cycle) // новая итерация
		return
	}

	return
}

//////////////////////////////////////////

// если нет главных циклов с пассивным мышлением, снять блокировку стимулов

////////////////////////////////////////////////////
/* если было прерывание, то восстановить инфу о прерванном цикле InterruptImage

 В режиме сновидений продолжается нормальная работа функции, но она ограничивается третьим уровнем.
т.е. могут выполняться моторные действия. Они не пройдут на Пульт, но в остальном функционал сохранится:
будет тот же период ожидания, выборка Правил, переактивации, все, без исключения.
и все идет как обычно с учетом Целей и поиском решений, если кончился цикл по последнему стимулу с пульта.

Уже было infoFunc10(cycle) //вспомнить самое значимое, что было для начала размышения
*/
func restoreDreamingProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "dreamingProcess"
	//		infoFunc20(cycle)// выбрать последний пустой кадр эпиз.памяти dreamingEpisodeHistoryID и запустить процесс осмысления

	// при возникновении - cycle.log+="Начать сновидение или мечтание(пассивное размышление, не в ответ на Стимул).<br>"

	//  если было прерывание, то восстановить инфу о прерванном цикле InterruptImage
	if InterruptMemory != nil && len(InterruptMemory) > 0 {
		// закончился цикл мышления и есть прерванные циклы - нужно вернутьсЯ к последнему.
		lastImg := rememberInterruptImage(cycle) // выборка прерванного размышления из стека и начало размышления об этом
		if lastImg != nil {
			cycle.log += "Вспоминание прерванного шага мышления: " + strconv.Itoa(cycle.ID) + "<br>"
			//return false // был перезапуск
		}
	}
	return false
}

////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/dreaming_functions.go
____________________________________________
/* функции пассивного режима мышления

 */

package psychic

/////////////////////////////////////////////////////
/*Найти кадр ветки эп.памяти для заданных условий (conditions[]), включая стимул
с наиболее экстремальной значимостью ИЛИ правил ИЛИ значимостью стимула (PARAMS[2]).

например, conditions := []int{
CurrentCommonBadNormalWell,
CurrentEmotionReception.ID,
detectedActiveLastProblemNodID,
extremImportanceObject.objID }
с данной значимостью effect.
Учительские правила не интересуют, нужна свой кадр, который можно модифицировать.
Но там могут быть самые разные EpisodicTreeNode.Action с данным эффектом (хотя и редко),
так что выдаем первую попавшуюся.

В conditions могут быть все условия обнулены!
*/
var extremMemArr []*EpisodicTreeNode

func findEpisodicBrangeFromObject(conditions []int) *EpisodicTreeNode {
	if conditions[3] == 0 {
		return nil // нужно искать данный стимул при любом состоянии условий!
	}
	extremMemArr = nil
	curLevelSimplify := 0
	// прямо проходим до ObjectID
	for extremMemArr == nil { // с каждым неудачным проходом упрошаем условия
		findEpisodicBrangeCondition(0, conditions, &EpisodicTree, conditions[3])
		if extremMemArr == nil {
			if curLevelSimplify < 3 {
				curLevelSimplify--
				conditions = toSimplifyCondition(conditions, curLevelSimplify)
			} else { // уже итак предельно упрощены условия
				return nil
			}
		} else {
			break // нашли
		}
	}

	extr := selectExtremalFrame(extremMemArr)
	if extr != nil {
		return extr
	}

	return nil
}
func findEpisodicBrangeCondition(level int, cond []int, root *EpisodicTreeNode, trigger int) {

	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children { // пока есть дочки
		if isEquivalentCondition(level, &child, cond) {
			if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
				if child.PARAMS[0] < 100 && child.Trigger == trigger { // не учительские правила
					extremMemArr = append(extremMemArr, &child)
					return
				}
			}
			// Если узел найден, продолжим рекурсивно искать в нем далее
			findEpisodicBrangeCondition(level+1, cond, &child, trigger)
			return
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return
}

// упостить условия поиска
func toSimplifyCondition(cond []int, levelSimplify int) []int {
	if levelSimplify == 1 {
		cond[3] = 0
	}
	if levelSimplify == 2 {
		cond[2] = 0
		cond[3] = 0
	}
	if levelSimplify == 3 {
		cond[1] = 0
		cond[2] = 0
		cond[3] = 0
	}
	//
	newPassiveEnvironment.mood = cond[0]
	newPassiveEnvironment.emotionID = cond[1]
	newPassiveEnvironment.problemNodID = cond[2]
	return cond
}

/////////////////////////////////////////////////////
/* в массиве eArr []*EpisodicTreeNode кадров выбрать один наиболее экстремальный

 */
func selectExtremalFrame(eArr []*EpisodicTreeNode) *EpisodicTreeNode {
	if eArr != nil && len(eArr) > 0 {
		var extrMin *EpisodicTreeNode
		var extrMax *EpisodicTreeNode
		for _, em := range eArr {
			if em.PARAMS[0] < extrMin.PARAMS[0] { // значимость парвила
				extrMin = em
			}
			// пишем все равно в em.PARAMS[0] т.к. это - просто буфер для значимостей
			if em.PARAMS[0] < extrMin.PARAMS[2] { // значимость стимула
				extrMin = em
			}

			if em.PARAMS[0] > extrMax.PARAMS[0] { // значимость парвила
				extrMax = em
			}
			// пишем все равно в em.PARAMS[0] т.к. это - просто буфер для значимостей
			if em.PARAMS[0] > extrMax.PARAMS[2] { // значимость стимула
				extrMax = em
			}
		}
		if extrMax.PARAMS[0] > -extrMin.PARAMS[0] {
			return extrMax
		} else {
			return extrMin
		}
	}
	return nil
}

///////////////////////////////////////////////////////

/*
	выбрать наиболее значимое из еще не угасших циклов кроме главного

Это - начало любого пассивного режима: о чем думать
Заполняется objectsOfPassiveToughtArr
*/

var objectsForPassiveToughtArr []int

func objectsFromPhoneCyclesArr() {
	objectsForPassiveToughtArr = nil
	// перебор фоновых циклов и выявление экстремальных объектов в них
	for _, v := range cyclesArr {
		if v == nil {
			continue
		}
		if v.isMainCycle {
			continue
		}
		if v.impObjID != 0 {
			objectsForPassiveToughtArr = append(objectsForPassiveToughtArr, v.impObjID)
		}
	}
}

//////////////////////////////////////////////////////////////////
/*находим недооцененные по значимости стимула неучительские кадры, т.е. с PARAMS[2]==0
первые 3 объекта с конца, они заполнятся в эпиз.памяти и не будут здесь возникать
*/
var unknownObjectsFrameArr []*EpisodicTreeNode

func unknownObjectsFromMemoryArr() {
	unknownObjectsFrameArr = nil
	fCount := 0 // число найденных
	from := len(EpisodicHistoryArr) - 1
	for i := from; i > 0; i-- {
		node, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[i].ID)
		if !ok {
			continue
		}
		if fCount == 3 {
			break
		}
		//неучительские node.PARAMS[0] < 100
		if node.PARAMS != nil && node.PARAMS[0] < 100 && node.PARAMS[2] == 0 {
			unknownObjectsFrameArr = append(unknownObjectsFrameArr, node)
			fCount++
		}
	}
}

////////////////////////////////////////////////////////////////

/*
Один шаг пассивного мышления.

Начиная с опорного кадра эпиз.памяти (*EpisodicTreeNode), а не кадра исторической.

Это - специализированная инфо-функция для мышления в цикле
с выдачей промежуточных результатов в инфо-картину.
Т.к. она не имеет цели и развивается сама по себе, то не вынесена в список инфо-функций, доступных для произвольного выбора.
Но она может быть активирована из какой-то инфо-функции (м.б. для пассивного мышления в фоне).

При проходе не ставится никакой цели (не требуется достичь позитива и т.п.).

Алгоритм действий с memFr *EpisodicTreeNode:
т.к. memFr *EpisodicTreeNode - уже найденный экстремальный кадр (func findEpisodicBrangeFromObject)
то для него просто смотрятся все следующие по истории кадры (в цепочке до прерывания),
из всех находится самый экстремальный,
и его стимул сохраняется в newPassiveEnvironment.extremID,
что и становится исходным для следующей итерации.

Если не находит ничего, то вернуть true
*/
var saveBeginEpisodicTreeNode *EpisodicTreeNode // сохранить кадр начала мышления для модификации после вспомианния
func gotoPassiveMaind(cycle *cycleInfo, memFr *EpisodicTreeNode) bool {
	saveBeginEpisodicTreeNode = memFr

	getNextHistoryEpisodicFrameArr(memFr)
	if fantasmArr != nil && len(fantasmArr) > 0 {
		extr := selectExtremalFrame(extremMemArr)
		if extr == nil {
			return true
		}
		// результаты итерации ЗАПИСАТЬ в newPassiveEnvironment

		/* первые задются в func toSimplifyCondition
		newPassiveEnvironment.mood=
		newPassiveEnvironment.emotionID=
		newPassiveEnvironment.problemNodID=
		*/
		if extr.PARAMS[0] < -5 || extr.PARAMS[2] < -8 {
			newPassiveEnvironment.danger = true
		}
		newPassiveEnvironment.stimulID = extr.Trigger
		newPassiveEnvironment.answerID = extr.Action
		if needOptimisationFrame != nil {
			newPassiveEnvironment.effect = extr.PARAMS[2]
			newPassiveEnvironment.extremID = extr.Trigger
			if extr.PARAMS[0] == 100 { // в учителском стимулом является Action
				newPassiveEnvironment.extremID = extr.Action
			}
		} else {
			newPassiveEnvironment.effect = extr.PARAMS[0]
			newPassiveEnvironment.extremID = extr.Trigger
		}
		newPassiveEnvironment.episodFrameID = extr.ID

		return false
	}

	return true // конец цепочки, исчерпание значимостей, закончить итерации
}

///////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////
/* найти следующие после memFr кадры эпизодов в исторической цепочке до прерывания (пустой кадр)
БЕЗ УСЛОВИЙ кроме настроения(что и дает диапазон фантазирования).
*/
var fantasmArr []*EpisodicTreeNode    // результирующие последующие кадры
var foundFrameArr []*EpisodicTreeNode // промежуточные исходные кадры
func getNextHistoryEpisodicFrameArr(memFr *EpisodicTreeNode) {
	if memFr == nil {
		return
	}
	foundFrameArr = nil
	fantasmArr = nil
	сondintion := []int{
		newPassiveEnvironment.mood,
		0,
		0,
		0}
	findFantasmArr(0, сondintion, &EpisodicTree, memFr.Trigger, memFr.Action)
	if foundFrameArr != nil && len(foundFrameArr) > 1 { //len(foundFrameArr) > 1 нужно чтобы находилось не только для исходного memFr!
		// выбираем последущие кадры в цепочках
		for _, frm := range foundFrameArr {
			nextArr := getNextHistiryEpisodicFrame(frm)
			if nextArr != nil && len(nextArr) > 0 {
				for _, f := range nextArr {
					fantasmArr = append(fantasmArr, f)
				}
			}
		}
	}

	return
}
func findFantasmArr(level int, cond []int, root *EpisodicTreeNode, trigger int, action int) {

	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children { // пока есть дочки
		if isEquivalentCondition(level, &child, cond) {
			if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
				if child.PARAMS[0] < 100 && child.Trigger == trigger && child.Action == action { // не учительские правила
					foundFrameArr = append(foundFrameArr, &child)
					return
				}
			}
			// Если узел найден, продолжим рекурсивно искать в нем далее
			findFantasmArr(level+1, cond, &child, trigger, action)
			return
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return
}

////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/emotions.go
____________________________________________
/* Эмоции.
Распознавание активности текущих Базовых контекстов в виде структур
- осмысленной значимости сочетаний активных базовых контекстов.
Произвольно возможна активация имеющегося образа, создание образа новых сочетаний.
*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
	"strconv"
	"strings"
)

///////////////////////////////////

// ////////////////////////////
func emotionsInit() {
	loadEmotionArr()
}

////////////////////////////////

/*
Образ сочетания базовых контекстов
*/
type Emotion struct {
	ID        int   // идентификатор данного сочетания контекстов
	BaseIDarr []int // сочетание базовых контекстов
}

////////////////////////////////

var CurrentCommonBadNormalWell = 0 // 1 - Похо, 2 - Норма, 3 - Хорошо
var oldCommonBadNormalWell = 0

// последняя определенная эмоция
var CurrentEmotionReception *Emotion
var oldCurrentEmotionReception *Emotion

var EmotionFromIdArr = make(map[int]*Emotion)

/*  создать новую эмоцию, если такой еще нет
 */
var lastEmotionID = 0

func createNewBaseStyle(id int, BaseIDarr []int, CheckUnicum bool) (int, *Emotion) {
	if CheckUnicum {
		oldID, oldVal := checkUnicumEmotion(BaseIDarr)
		if oldVal != nil {
			//CurrentEmotionReception=oldVal
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastEmotionID++
		id = lastEmotionID
	} else {
		//		newW.ID=id
		if lastEmotionID < id {
			lastEmotionID = id
		}
	}

	var node Emotion
	node.ID = id
	node.BaseIDarr = BaseIDarr

	EmotionFromIdArr[id] = &node

	if doWritingFile {
		SaveEmotionArr()
	}

	//CurrentEmotionReception=&node  - только при активации дерева актоматизмов!!!

	return id, &node
}
func checkUnicumEmotion(bArr []int) (int, *Emotion) {
	for id, v := range EmotionFromIdArr {
		if v == nil {
			continue
		}
		if lib.EqualArrs(bArr, v.BaseIDarr) {
			return id, v
		}
	}

	return 0, nil
}

////////////////////////////////////////

// ///////////////  сохранить образы сочетаний базовых стилей
func SaveEmotionArr() {
	var out = ""
	for k, v := range EmotionFromIdArr {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		for i := 0; i < len(v.BaseIDarr); i++ {
			out += strconv.Itoa(v.BaseIDarr[i]) + ","
		}
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/emotion_images.txt", out)
}

// ////////////////  загрузить образы сочетаний базовых стилей
func loadEmotionArr() {
	EmotionFromIdArr = make(map[int]*Emotion)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/emotion_images.txt")
	cunt := len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		s := strings.Split(p[1], ",")
		var BaseIDarr []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			bc, _ := strconv.Atoi(s[i])
			BaseIDarr = append(BaseIDarr, bc)
		}
		var saveDoWritingFile = doWritingFile
		doWritingFile = false
		createNewBaseStyle(id, BaseIDarr, false)
		doWritingFile = saveDoWritingFile
	}
	return
}

/////////////////////////////////////////////////////////////////////

// //////////////////////////////////////////////////////////////////
// Описать словами текущую эмоцию
func getEmotonsComponentStr(em *Emotion, strID bool) string {
	var out = " "

	if strID {
		out = "(ID=" + strconv.Itoa(em.ID) + ") "
	}
	if em == nil {
		return "НЕТ"
	}
	for i := 0; i < len(em.BaseIDarr); i++ {
		if i > 0 {
			out += ", "
		}
		out += gomeostas.GetBaseContextCondFromID(em.BaseIDarr[i])
	}

	return out
}

//////////////////////////////////////////

// последняя испытанная эмоция в виде строки
func GetCurrentEmotionReception() string {
	if CurrentEmotionReception == nil {
		return "Эмоция еще не определена."
	}
	return getEmotonsComponentStr(CurrentEmotionReception, true)
}

///////////////////////////////////////////

// есть ли данный компонент в текущей эмоции
func existsBaseContext(baseContID int) bool {
	if CurrentEmotionReception == nil {
		return false
	}
	for i := 0; i < len(CurrentEmotionReception.BaseIDarr); i++ {
		if CurrentEmotionReception.BaseIDarr[i] == baseContID {
			return true
		}
	}
	return false
}

//////////////////////////////////////////////

// для пульта
/* Уже есть в automatism_tree_pult_show.go func GetStrnameFromBaseImageID(id int)(string)
func GetEmotionContexts(emID int)string{
	em,ok:=EmotionFromIdArr[emID]
if !ok{
	return "Нет эмоции с ID="+strconv.Itoa(emID)
}
	out:=getEmotonsComponentStr(em, false)

	return out
}*/
//////////////////////////////////////////

____________________________________________

BOT/brain/psychic/episodic_memory_pult.go
____________________________________________
/* Показ эпиз.памяти на Пульте

 */

package psychic

import (
	"BOT/lib"
	"sort"
	"strconv"
)

/*
	100 последних Правил, записанных в кадрах эпизодической памяти, включая "учительские"
	var idEpArr = make(map[int]param)// массивы params для данного ID кадра памяти

Если objID !=0 то выдать не 100, а все и только где есть объект objID
*/
func GetCur100lastRules(objID int) string {

	hStr := "<b>Историческая последовательность 100 последних кадров эпизодической памяти:</b><br>"
	if objID > 0 {
		hStr = "<b>Модель понимания: значимости кадров эпизодической памяти для объекта ID=" + strconv.Itoa(objID) + ":</b><br>"
	}

	// последний индекс исторической памяти EpisodicHistoryArr[]
	lastI := len(EpisodicHistoryArr) - 1
	var node *EpisodicTreeNode
	out := "<hr><br>параметры Правил, записанных в кадрах эпизодической памяти, включая учительские." +
		"<table cellpadding=0 cellspacing=0 border=1 class='main_table'><tr>" +
		"<th class='table_header' title='ID кадра эпизодической памяти'>ID</th>" +
		"<th class='table_header' title='1 - Похо, 2 - Норма, 3 - Хорошо'>BaseID</th>" +
		"<th class='table_header' title='ID эмоции'>EmotionID</th>" +
		"<th class='table_header' title='ID дерева проблем'>NodePID</th>" +
		"<th class='table_header' title='Стимул с Пульта'>Trigger</th>" +
		"<th class='table_header' title='Ответное действие'>Action</th>" +
		"<th class='table_header' title='Эффет от действия'>Effect</th>" +
		"<th class='table_header' title='Число подтверждений при записи такого кадра'>Уверенность</th>" +
		"<th class='table_header' title='Значимость образа в модели понимания'>Значимость</th></tr>"
	rCount := 0
	curID := 0
	var curIdArr []int
	style := "style='font-size:19px;font-weight:bold;cursor:pointer'"
	limit := 100
	if objID > 0 {
		limit = 100000000
	}
	for i := lastI; i >= 0 && rCount < limit; i-- {
		curID = EpisodicHistoryArr[i].ID
		if curID == -1 {
			hStr += frameStr(-1, 0)
			continue
		}

		node0, ok := ReadeEpisodicTreeNodeFromID(curID)
		if !ok {
			continue
		}
		if objID > 0 {
			if node0.Trigger != objID && node0.Action != objID {
				continue
			}
		}
		node = node0

		pars := node.PARAMS
		if len(pars) == 0 {
			continue
		}

		if !lib.ExistsValInArr(curIdArr, curID) { // это таблица-расшифровка параметров кадров памяти, тут не нужна история
			curIdArr = append(curIdArr, curID)
		}
		rCount++
		hStr += frameStr(curID, 0)
	}

	if rCount == 0 {
		out += "<tr><td colspan=7>Не найдены Правила в эпизодической памяти.</td></tr>"
	} else {
		sort.Ints(curIdArr)
		for i := 0; i < len(curIdArr); i++ {
			node0, ok := ReadeEpisodicTreeNodeFromID(curIdArr[i])
			if !ok {
				continue
			}
			node = node0

			pars := node.PARAMS
			bg := "#ffffff" // фон строки учительского правила - bg=="#FFEBE4"
			ttr := ""
			// Показываем  node.PARAMS
			effect := pars[0]
			if effect == 100 {
				bg = "#FFEBE4"
				ttr = "Учительское правило"
			}
			out += "<tr style='background-color:" + bg + ";' title='" + ttr + "'>" +
				"<td >" + strconv.Itoa(curIdArr[i]) + "</td>" +
				"<td " + style + " title='1 - Похо, 2 - Норма, 3 - Хорошо' >" + strconv.Itoa(node.BaseID) + "</td>" +
				"<td " + style + " onClick='show_emotion(" + strconv.Itoa(node.EmotionID) + ")'>" + strconv.Itoa(node.EmotionID) + "</td>" +
				"<td " + style + " onClick='show_problem_tree(" + strconv.Itoa(node.NodePID) + ")'>" + strconv.Itoa(node.NodePID) + "</td>" +
				"<td " + style + " onClick='show_object(1," + strconv.Itoa(node.Trigger) + ")'>" + strconv.Itoa(node.Trigger) + "</td>" +
				"<td " + style + " onClick='show_object(1," + strconv.Itoa(node.Action) + ")'>" + strconv.Itoa(node.Action) + "</td>" +
				"<td " + style + ">" + strconv.Itoa(effect) + "</td>" +
				"<td " + style + ">" + strconv.Itoa(pars[1]) + "</td>"
			if effect == 100 { // для учительского правила
				out += "<td style='font-size:19px;font-weight:bold;cursor:pointer;color:blue' onClick='get_undestand_model(" + strconv.Itoa(node.Action) + ")' title='Значимость node.Action'>" + strconv.Itoa(pars[2]) + "</td>"
			} else {
				out += "<td style='font-size:19px;font-weight:bold;cursor:pointer;color:blue' onClick='get_undestand_model(" + strconv.Itoa(node.Trigger) + ")'  title='Значимость node.Trigger'>" + strconv.Itoa(pars[2]) + "</td>"
			}
			out += "</tr>"
		}
	}
	out += "</table>"
	out = hStr + out

	return out
}

func frameStr(id int, kind int) string {

	if id == -1 {
		return "<div class='frameEm' title='пустой кадр - конец темы'>&nbsp;</div>"
	}
	if kind == 1 {
		return "<div class='frameEv' title='Кадр объекта Модели понимания'>" + strconv.Itoa(id) + "</div>"
	}
	return "<div class='frameEP'>" + strconv.Itoa(id) + "</div>"
}

///////////////////////////////////////////////////////////
/* модель понимания объекта с ID
Найти все вхождения объекта в эпиз.память и выдать таблицу как для func GetCur100lastRules()
но только для данного объекта
*/
func Get_undestand_model_from_object(objID int) string {

	return GetCur100lastRules(objID)
}

//////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/episodic_memory_rules.go
____________________________________________
/*  фиксация Правил в эпизодической памяти

Обычно Правила имеют Стимул (от оператора), ответное действие Beast и эффект
но если действие Beast было не в ответ на Стимул, а самостоятельным (после провокации func infoFunc31)
то ID Стимула в Правиле будет равен нулю.

*/

package psychic

import (
	"BOT/brain/action_sensor"
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
)

///////////////////////////////////////////////////////////

// ЗАПИСЬ ЭПИЗОДОВ С ПРАВИЛАМИ
/*
 образ действий оператора Стимул:	curStimulImageID и есть Ответ: curActiveActions
*/
func fixEpizMemoryRules(lastCommonDiffValue int) {
	if LastAutomatizmWeiting == nil {
		return
	}

	if wasRunProvocationFunc { //была  провокация оператора на действие func infoFunc31
		curStimulImageID = 0 // без стимула от оператора
	} else {
		// curStimulImageID - образ действий оператора перед Ответом для записи Правила
		if curStimulImageID == 0 {
			return
		}
		if curStimulImage == nil { // где-то сбрасывается!!??
			//		curStimulImage=ActionsImageArr[curStimulImageID]
			aImage, ok := ReadeActionsImageArr(curStimulImageID)
			if ok {
				curStimulImage = aImage
			}
		}
		if curStimulImage.ActID == nil && curStimulImage.PhraseID == nil { // не писать Правила с пустым Стимулом
			return
		}
	}

	answerID := 0 // ответ Beast
	if LastAutomatizmWeiting != nil {
		answerID = LastAutomatizmWeiting.ActionsImageID // ответный образ действий Beast
		_, ok := ReadeActionsImageArr(answerID)
		if !ok {
			return
		}

		if !wasRunProvocationFunc {
			if curStimulImageID == answerID { //попугайство просьбы показать как нужно
				return
			}
		}
	}
	if lastRunVolutionAction != nil {
		answerID = lastRunVolutionAction.ID // ответный образ действий Beast
		lib.MapFree(MapGwardAutomatizmNextStringFromID)
		if AutomatizmNextStringFromID[answerID] == nil {
			return
		}
	}

	if answerID == 0 {
		return
	}

	if LastAutomatizmWeiting.NextID > 0 { // автоматизм с довеском акций типа AmtzmNextString
		// создать AmtzmNextString из всех действий автоматизма
		asID, _ := createNextStringFromAutomatizm(LastAutomatizmWeiting)
		answerID = prefixActionIdValue + asID // увеличить ID на метку AmtzmNextString
	}
	if lastRunVolutionAction != nil { // было произвольное действие
		answerID = prefixActionIdValue + answerID // увеличить ID на метку AmtzmNextString
	}

	if !wasRunProvocationFunc {
		if detectedActiveLastNodPrevID == 0 || detectedActiveLastUnderstandingNodPrevID == 0 {
			return
		}
	}
	// Запись Правила (именно detectedActiveLastNodPrevID а не detectedActiveLastNodID)

	/*	Зачем писать ПРЯМОЕ одиночное правило, если есть автоматизм на тот же стимул? Учительское правило как пробник - другое дело
		Имхо: одиночное правило - только учительское, чтобы по нему потом пробник сделать. Анализ по правилам делается только по групповым - с целью посмотреть, чем может закончится диалог, позитивом или негативом
		Групповое правило - это динамический стимул, реакция на цепочку стимул-ответов типа:
		Ты кто? - бот, уверен? - да, [поощрить]
		Кто ты? - Бог, уверен? - да, [наказать]
		На один и тот же статический стимул  "уверен" выдается действие "да" - и получает подзатыльник или пряник. Чтобы решить проблему как реагировать, нужно смотреть всю цепочку диалога, представив ее как динамичекйи стимул
		Он то как раз разный - потому и реакции разные. Можно сказать, что бота наказывают не заего ответ на "уверен", а за ответ на "ты кто?" и "кто ты?" - или за любое другое отличие в цепочке звеньев стимул-ответов динамического стимула
	*/
	//currentRulesID, _ = createNewRules(0, detectedActiveLastNodPrevID,detectedActiveLastUnderstandingNodPrevID,[]int{TriggerAndAction},true)
	//if currentRulesID == 0{return 0}

	//lib.WritePultConsol("<span style='color:green'>Записано <b>ПРАВИЛО № "+strconv.Itoa(currentRulesID)+"</b></span>") // уже есть сообщение в createNewRules()

	//////////// ЗАПИСАТЬ В ДЕРЕВО НОВЫЙ ЭПИЗОД
	// curStimulImageID - предыдущий стимул (текущий - curActiveActionsID)
	//usedOldCondition = true    // испольховать предыдущее значение условий (до стимула от оператора)
	if wasRunProvocationFunc { //после провокации func infoFunc31 нет образа стимула т.к. beast провоцирует при отсуствии стимула
		saveNewEpisodic(0, answerID, lastCommonDiffValue, 0)
	} else {
		// prevStimulsEffect - т.к. первый стимул уже перекрыт ответом оператора на действия beast
		saveNewEpisodic(curStimulImageID, answerID, lastCommonDiffValue, prevStimulsEffect)
	}

	return
}

///////////////////////////////////////////////////////////////////////

/*
	запоминает авторитерный ответ Оператора на совершенное действие - учительское правило.

- одиночное Правило как Оператор отвечает на действиЯ Beast
>> авторитетное Правило всегда имеет эффект +1
так нельзя, иначе негативное действие оператора будет расценено как положительное и в дальнейшем будет выбираться по этому признаку
то есть, получив негативный ответ от оператора, бот все равно будет его давать, потому как он записан у него как положительный

Такое Правило используется в случае отсуствия решения как отвечать,
т.к. не пришется групповое Правило дял точного бездумного реагирования
(хотя на уровне эпиз.памяти и можно вычленять такие групповые Правила,
выделяя Стимул следующего Правила как ответ на действия Beast).
*/
func fixEpizMemoryTeachRules(lastCommonDiffValue int) {
	if curActiveActions == nil { // так было когда использовалась func LastAutomatipmCorrection (теперь ее нет)
		// Пусть на всякий случай будет: вытащить стимул из базовой рецепции:
		if action_sensor.PultActionPulsCount == PulsCount { // только если оператор только что ответил, а не берется какая-то прежняя акция
			curActiveActionsID, curActiveActions = CreateNewlastActionsImageID(0, 0, action_sensor.CheckCurActions(), wordSensor.CurrentPhrasesIDarr, wordSensor.DetectedTone, wordSensor.CurPultMood, true)
			wasChangingMoodCondition() // раз не успевает...
		}
		if curActiveActions == nil {
			return
		}
	}

	// curActiveActionsID - образ действий оператора после Ответа для записи Правила
	if curActiveActionsID == 0 {
		return
	}

	if curActiveActions.ActID == nil && curActiveActions.PhraseID == nil { // не писать Правила с пустым Стимулом
		return
	}

	/* Если есть ли автоматизм с действием оператора curActiveActionsID, и если у него atmtzm.Usefulness<0 - постепенно снять блокировку
	потому как это - новое авторитарное подтвержение полезности.
	Но если была запущена func13 то попугайский автоматизм-переспрос разблокируется на следующем шаге. Поэтому разблокировка
	только в случае, когда нет запуска func13.
	Так же нельзя разблокировать, если только что было применено наказание кнопками действий, иначе оно нивелируется
	*/
	answerID := 0 // // образ действий Beast перел ответом Оператора
	if prevLastAutomatizmWeiting != nil {
		// предыдущий момент запуска автоматизма был задолго от последующего действия оператора
		if (PulsCount - prevLastDetectedActiveLastPulsCount) > 25 {
			return
		}
		if curFunc13ID == 0 && lastCommonDiffValue >= 0 {
			checkForUnbolokingAutomatizm(curActiveActionsID)
		}
		answerID = prevLastAutomatizmWeiting.ActionsImageID // ответный образ действий Beast
		_, ok := ReadeActionsImageArr(answerID)
		if !ok {
			return
		}
		if prevLastAutomatizmWeiting.NextID > 0 {
			asID, _ := createNextStringFromAutomatizm(prevLastAutomatizmWeiting)
			answerID = prefixActionIdValue + asID
		}
	}

	if prevLastRunVolutionAction != nil {
		answerID = prevLastRunVolutionAction.ID // ответный образ действий Beast
		lib.MapFree(MapGwardAutomatizmNextStringFromID)
		if AutomatizmNextStringFromID[answerID] == nil {
			return
		}
	}

	if lastRunVolutionAction != nil { // было произвольное действие
		answerID = prefixActionIdValue + answerID // увеличить ID на метку AmtzmNextString
	}

	if answerID == 0 {
		return
	}

	if action_sensor.PultActionPulsCount == PulsCount { // только если оператор только что ответил, а не берется какая-то прежняя акция

		// уже есть стимул от оператора curActiveActionsID незачем получать новый из базовых сенсоров
		//curAct, _ := CreateNewlastActionsImageID(0, 0, action_sensor.CheckCurActions(), wordSensor.CurrentPhrasesIDarr, wordSensor.DetectedTone, wordSensor.CurPultMood, true)

		// в стадиях до 4 обнуляется в automatizmTreeActivation()
		// очистить фразу после использования, чтобы не влияла на следующую активность
		wordSensor.CurrentPhrasesIDarr = nil // остается еще wordSensor.CurretWordsIDarr

		//  ЗАПИСАТЬ В ДЕРЕВО НОВЫЙ ЭПИЗОД  эффект =100 - метка, что это - учительское правило.
		usedOldCondition = true // испольховать предыдущее значение условий (до стимула от оператора)
		saveNewEpisodic(answerID, curActiveActionsID, 100, globalStimulsEffect)
	}

	return
}

/////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/episodic_memory_tree.go
____________________________________________
/*  Дерево эпизодической памяти.
для записи моторных правил для данных условий и фиксации значимостей образов стимулов.
Стало ясно, что моторная эпиз.память формируется в районе гиппокампа, а ментальная - где-то в лобных долях (там тоже есть циклы).
К дереву прилагается массив для фиксации исторической последовательности эпизодов.
В эпиз.памяти есть все для извлечения информации моделей понимания (в understanding_model.go) - системы значимостей образа в разных условиях.

Дерево легко использовать для
1) предказаний, что будет, если после данного стимула применить такой-то ответ
2) выбора ответов с позитивным эффектом

В дереве 5 узлов, до узла Action включительно:
NodeAID|NodeSID|NodePID|Trigger|Action|PARAMS
т.е. после Action идут его дочки типа PARAMS (числом от ни одной до сколько угодно)

значения PARAMS[] - это свойства эпизода: Effect|Count(уверенность)|stimulsEffect(значимость стимула)
Если при записи нового эпизода в дерево уже есть такая ветка, то эффект правила усредняется (func averageEffect)
Сила эффекта правил в зависимости от count отпределеяется func getWpower
Сила значимости стимула в зависимости от count отпределеяется func getОpower

В файл пишется:
ID|ParentID|BaseID|EmotionID|NodePID|Trigger|Action#Effect|Count|stimulsEffect
Нет смысла выносить PARAMS[] в отдельныю структуру со связью по ID
т.к. память это не сэкономит, но приведет к лишнему геммору.

Дерево не активируется по каким-то стимулам, в него просто делается запись Правил
и используется для получения информации.

ПРИНЦИП ПОИСКА НАИБОЛЕЕ СТАТИСТИЧЕСКИ ВЕРНОГО ДЕЙСТВИЯ (GPT):
Eсть историческая лента прошлых кадров. По дереву ищутся кадры, максимально похожие на текущую ситуацию.
Начинается от них просмотр далее чтобы найти действие с позитивным результатом.
Каждая цепоска проходится или до позитива (он может быть или сразу или через несколько негативов)
или до минутного перерыва (считаешь, что опят на этом кончился неудачно).
Все найденные позитивные цепочки сравниваются и выбирается с самым большим позитивом.
Начинается действие первого шага, пусть негативного, но скоро же будет позитив.
Так реализуется принцип GPT.

ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ:
1. saveNewEpisodic(actionsImageID, autmtzm.ActionsImageID, effect, averageEffect) - новый кадр эпизодической памяти, сохраняющий Правил
2. saveNewEpisodic(answerID, curAct, 100, averageEffect) - учительское правило
3. saveNewEpisodic(curActions.ID, 0, 0, 0) - записать пустое Правило (нет Action)

Т.к. в дереве эп.памяти не может быть дубликатов, то каждый узел уникален, а время записи памяти фиксируется в истории EpisodicHistoryArr

*/
/////////////////////////////////////////////////////////////////////

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

///////////////////////////////////////////

type EpisodicTreeNode struct { // узел дерева Эпизодической памяти
	ID int
	// Узлы дерева:
	BaseID    int // 1 - Похо, 2 - Норма, 3 - Хорошо
	EmotionID int // эмоция
	/* субъективный узел активной ветки дерева проблем (последний активировавшийся)
	По нему можно найти:
	node,ok:=ReadeProblemTreeNodeFromID()
	if ok{
	NodeAID:=node.autTreeID //конечный узел активной ветки дерева моторных автоматизмов
	NodeSID:=node.situationTreeID //конечный узел активной ветки дерева ситуации
	}*/
	NodePID int

	// Cтимул (полный образ Стимула с Пульта) = ActionsImage.ID
	Trigger int

	// Образ ответных действий - ActionsImage.ID
	Action int
	// Свойства данного узла - массив PARAMS:
	PARAMS []int
	/* [3] иммет три значения:
	Effect int // эффект от действий: (меньше 0) или 0 или (больше 0) Effect учительского Правила ==100.
	Count int // всегда>0 - число усреднений Effect  (используется Count) - уверенность в Правиле и значимости стимула.
	stimulsEffect int // значимость стимула усредняется с каждой новой записью в дерево (используется Count).
	*/

	//Связь узлов дерева:
	Children   []EpisodicTreeNode // дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID   int                // ID родителя
	ParentNode *EpisodicTreeNode  // адрес родителя
}

// ////////////////////////////////////////////////////
var EpisodicTree EpisodicTreeNode

// var EpisodicTreeNodeFromID=make(map[int]*EpisodicTreeNode)
var EpisodicTreeNodeFromID []*EpisodicTreeNode // сам массив
// запись члена
func WriteEpisodicTreeNodeFromID(index int, value *EpisodicTreeNode) {
	addEpisodicTreeNodeFromID(index)
	EpisodicTreeNodeFromID[index] = value
}
func addEpisodicTreeNodeFromID(index int) {
	if index >= len(EpisodicTreeNodeFromID) {
		newSlice := make([]*EpisodicTreeNode, index+1)
		copy(newSlice, EpisodicTreeNodeFromID)
		EpisodicTreeNodeFromID = newSlice
	}
}

// считывание члена
func ReadeEpisodicTreeNodeFromID(index int) (*EpisodicTreeNode, bool) {
	if index < 0 {
		return nil, false
	}
	if index >= len(EpisodicTreeNodeFromID) || EpisodicTreeNodeFromID[index] == nil {
		return nil, false
	}
	return EpisodicTreeNodeFromID[index], true
}

//////////////////////////////////////////////////////////////////////

/*
Связь узлов историческая - ID конечных узлов веток EpisodicTree подряд по порядку возникновения новых.
Записывается в файл "/memory_psy/episodic_history.txt" - сплошная строка EpisodicTreeNode.ID с разделителем "|"
В случе добавления newPARAMS в существующий EpisodicTreeNode пишется его EpisodicTreeNode.ID
так что в EpisodicHistoryArr могут появляться несколько EpisodicTreeNode.ID.

Пустые кадры (с ID == -1) разделяют цепочки памяти одной темы.

Поиск в EpisodicHistoryArr нужного EpisodicTreeNode.ID
При оптимизации во сне порядок сохраняется, но могут вырезаться отдельные составляющие.
При вырезании из EpisodicHistoryArr пишется в файл episodic_history.txt новая строка
и т.о. EpisodicHistoryArr []int остается с непрерывной цепочкой индексов.

Найти в массиве EpisodicHistoryArr[] индексы всех членов по значению:
iArr:=lib.FindIndexes(EpisodicHistoryArr,ID)

Запись в файле: ID1,LifeTime1|ID2,LifeTime2 ...
*/
type History struct {
	ID       int // ID EpisodicTreeNode
	LifeTime int // время появления узла в пульсах от рождения
}

var EpisodicHistoryArr []History

///////////////////////////////////////////////////////

/*
	Элемент отдельного Правила

для вывода целевой цепочки Правил и т.п.
*/
type Rule struct { //
	Trigger    int // Cтимул (действий оператора)
	Action     int // образ ответных действий - всегда ActionsImage
	Effect     int // эффект от действий: -1 или 0 или 1
	Count      int // всегда>0 - число усреднений Effect - уверенность в Правиле
	Importence int // значимость стимула (Trigger если Effect<100 или Action если Effect==100)
}

///////////////////////////////////////////////////////

// ////////// ЗАПИСАТЬ В ДЕРЕВО НОВЫЙ ЭПИЗОД
var lastEpisodicMemID = 0

/*
	если нужно испольховать предыдущее значение условий (до стимула от оператора) - для прямого правила func fixEpizMemoryRules

т.к. послед действия оператора может сильно измениться базовый контекст и эмоция.
*/
var usedOldCondition = false

func saveNewEpisodic(Trigger int, Action int, Effect int, stimulsEffect int) {

	//	NodeAID=65;NodeSID=38;NodePID=8;Trigger=1;Action=1;Effect=4;stimulsEffect=1;
	//	NodeAID=65;NodeSID=38;NodePID=8;Trigger=1;Action=2;Effect=4;stimulsEffect=1;

	var BaseID = 0
	var EmotionID = 0
	var NodePID = 0                                                // ID проблемы
	if usedOldCondition && oldDetectedActiveLastProblemNodID > 0 { // только если уже есть предыдущие значения!
		BaseID = oldCommonBadNormalWell
		EmotionID = oldCurrentEmotionReception.ID
		NodePID = oldDetectedActiveLastProblemNodID // ID проблемы
	} else {
		BaseID = CurrentCommonBadNormalWell
		EmotionID = CurrentEmotionReception.ID
		NodePID = detectedActiveLastProblemNodID // ID проблемы
	}

	usedOldCondition = false

	//	getEpisodesArrFromConditions(0)

	// !!!! после провокации func infoFunc31 нет Стимула!
	//if Action == 0 { // может быть пустое правило!
	//	return
	//}
	params := []int{Effect, 1, stimulsEffect}
	var condArr = []int{BaseID, EmotionID, NodePID, Trigger, Action}

	//проверяются дубликаты
	idOld, nodeOld := checkEpisodicBranchFromCondition(BaseID, EmotionID, NodePID, Trigger, Action)
	if idOld > 0 {
		EpisodicHistoryArr = append(EpisodicHistoryArr, History{idOld, LifeTime})
		// усреднить эффект
		averageEffect(nodeOld, Effect, stimulsEffect)
		params = nodeOld.PARAMS
	}

	if Effect != 100 && params != nil {
		if params[0] > 10 {
			params[0] = 10
		}
		if params[0] < -10 {
			params[0] = -10
		}
	}

	lastEpisodicMemID = addEpisodicFromNodeIDsToBrange(0, 0, condArr, params)
	//lastEpisodicMemID - ID последнего кадра

	if idOld == 0 && lastEpisodicMemID >= 0 { // не давать записывать кадры прерывания цепочки: lastEpisodicMemID ==-1
		EpisodicHistoryArr = append(EpisodicHistoryArr, History{lastEpisodicMemID, LifeTime})
	}

	return
}

// ////////////////////////////////////////
// усреднить эффект в PARAMS узла
func averageEffect(node *EpisodicTreeNode, effect int, stimulsEffect int) {
	if node.PARAMS == nil {
		return
	}
	// вес нового эффекта
	count := node.PARAMS[1] + 1
	if count == 0 {
		lib.TodoPanic("func averageEffect НУЛЕВОЕ ЗНАЧЕНИЕ count")
		return
	}
	if effect != 100 {
		// усреднить эффект правила
		w := int(((node.PARAMS[0] * (count - 1)) + effect) / count)
		if w > 10 {
			w = 10
		}
		if w < -10 {
			w = -10
		}
		node.PARAMS[0] = w
	}
	// усреднить значимость стимула
	w := int(((node.PARAMS[2] * (count - 1)) + stimulsEffect) / count)
	if w > 10 {
		w = 10
	}
	if w < -10 {
		w = -10
	}
	node.PARAMS[2] = w
}

///////////////////////////////////////////////////////////////////////////////////

/*
	Для создания нового узла ветки дерева

Возвращает ID узла и указатель на него - в любом случае, создан ли новый узел или найден такой же уже имеющийся.
*/
var lastIDNodeTree = 0 // счетчик ID узлов, хранит ID последнего созданного узла
func createEpisodicNodeTree(parent *EpisodicTreeNode, id int, BaseID int, EmotionID int, NodePID int, Trigger int, Action int, PARAMS []int) (int, *EpisodicTreeNode) {
	if parent == nil {
		return 0, nil
	}

	if id == 0 {
		lastIDNodeTree++
		id = lastIDNodeTree
	} else {
		if lastIDNodeTree < id {
			lastIDNodeTree = id
		}
	}

	var node EpisodicTreeNode
	node.ID = id
	node.ParentNode = parent
	node.ParentID = parent.ID
	node.BaseID = BaseID
	node.EmotionID = EmotionID
	node.NodePID = NodePID
	node.Trigger = Trigger
	node.Action = Action
	node.PARAMS = PARAMS

	parent.Children = append(parent.Children, node)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *EpisodicTreeNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
			//		newN.Children=append(newN.Children, node)
		}
	}

	//EpisodicTreeNodeFromID[node.ID]=&node
	WriteEpisodicTreeNodeFromID(node.ID, &node)

	//WriteEpisodicTreeNodeFromID(parent.ID, parent)

	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в EpisodicTreeNodeFromID:
	// ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
	updatingEpisodicTreeNodeFromID(parent) // здесь потому, что при загрузке из файла нужно на лету получать адреса
	return id, newN
}

//////////////////////////////////////////////////////////

// ////////////////////////////////
func checkEpisodicBranchFromCondition(BaseID int, EmotionID int, NodePID int, Trigger int, Action int) (int, *EpisodicTreeNode) {
	cond := []int{BaseID, EmotionID, NodePID, Trigger, Action}
	//maxLev:=5
	id, level := findEpisodicBrange(0, cond, &EpisodicTree)
	if id > 0 {
		// найти уровень lev0 ненулевых параметров
		lev0 := getTrueLevel(cond)
		if lev0 > level {
			return 0, nil
		}
		//if maxLevel==maxLev{// есть такая ветка, просто возвращаем ее значения

		//node,ok:=EpisodicTreeNodeFromID[id]
		node, ok := ReadeEpisodicTreeNodeFromID(id)
		if ok {
			return id, node
		} else { // такого не должно быть
			lib.TodoPanic("В func FindEpisodicTreeNodeFromCondition должно быть значение карты EpisodicTreeNodeFromID.") //вызвать панику
		}

		//}else{// нужно дорастить ветку
		//	return 0,nil
		//}
	} else { // ID==0 вообще нет совпадений, нужно наращивать с основы
		return 0, nil
	}

	return 0, nil
}

// ////////
// найти уровень lev0 ненулевых параметров из массива условий
func getTrueLevel(cond []int) int {
	lev0 := 0
	for i := 0; i < len(cond); i++ {
		if cond[i] == 0 {
			break
		}
		lev0++
	}
	return lev0
}

// //////////
// рекурсивно корректируем адреса всех узлов
// ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
func updatingEpisodicTreeNodeFromID(rt *EpisodicTreeNode) {
	if rt.ID > 0 {
		rt.ParentNode = EpisodicTreeNodeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		_, ok := ReadeEpisodicTreeNodeFromID(rt.ParentID)
		if ok {
			WriteEpisodicTreeNodeFromID(rt.ID, rt)
		}
	}
	if rt.Children == nil { // конец ветки
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		updatingEpisodicTreeNodeFromID(&rt.Children[i])
	}
}

///////////////////////////////////////////////////////////

// //////////////////////////////////////////////
// получить массив PARAMS[3] последнего узла дерева
// Только узел c ActionID имеет PARAMS[][]
func getEpisodicPARAMS(nodeID int) []int {

	//var actionNode,ok=EpisodicTreeNodeFromID[nodeID]
	actionNode, ok := ReadeEpisodicTreeNodeFromID(nodeID)
	if !ok {
		return nil
	}

	if actionNode.Action == 0 {
		return nil
	}

	return actionNode.PARAMS //[]int{}
}

////

/////////////////////////////////////////////////////////////////////////

/*
	Доращивание ветки, начиная с заданного узла fromID по массиву всех значений для ветки

newPARAMS - добавить к ветке, если она существует новый массив Effect|LifeTime
Возвращает ID конечного узла ветки.
*/
func addEpisodicFromNodeIDsToBrange(fromID int, lastLevel int, condArr []int, newPARAMS []int) int {

	var bNode *EpisodicTreeNode
	if fromID > 0 {
		//node, ok := EpisodicTreeNodeFromID[fromID]
		node, ok := ReadeEpisodicTreeNodeFromID(fromID)
		if !ok {
			return 0
		}
		bNode = node
	} else { // не определен начальный узел, хотя он может и быть
		bNode = &EpisodicTree
	}

	lastNodeID, _ := addEpisodicNodesToBrange(bNode, lastLevel, condArr, newPARAMS)

	return lastNodeID
}

/*
рекурсивно создать все недостающие уровни ветки по значениям condArr []int
*/
func addEpisodicNodesToBrange(fromNode *EpisodicTreeNode, level int, condArr []int, newPARAMS []int) (int, *EpisodicTreeNode) {
	if fromNode == nil {
		return 0, nil
	}
	if level >= len(condArr) {
		return fromNode.ID, fromNode
	}
	//vArr := make([]int, len(condArr))// все vArr[n] имеют нулевые значения
	vArr := []int{0, 0, 0, 0, 0}
	for i := 0; i <= level; i++ { // заполнить vArr до текущего уровня включительно, остальные - оставить нулями
		vArr[i] = condArr[i]
	}
	// с каждым уровнем vArr[n] добавляется новое значение для создания узла ветки следующего уровня.
	// если такой узел есть (checkUnicum==true), то просто вернет его node
	var node *EpisodicTreeNode
	var pars []int

	//проверяются дубликаты потому, что новый узел нужно делать только если такого еще нет!!!
	idOld, nodeOld := checkEpisodicBranchFromCondition(vArr[0], vArr[1], vArr[2], vArr[3], vArr[4])

	//node.PARAMS=append(node.PARAMS,pars)
	if idOld > 0 {
		node = nodeOld
		if level == 4 && newPARAMS != nil {
			// усреднить эффект
			averageEffect(node, newPARAMS[0], newPARAMS[2])
		}
	} else { // НОВЫЙ
		if level == 4 {
			pars = newPARAMS
		}

		if pars != nil && pars[0] != 100 {
			if pars[0] > 10 {
				pars[0] = 10
			}
			if pars[0] < -10 {
				pars[0] = -10
			}
		}

		_, node = createEpisodicNodeTree(fromNode, 0, vArr[0], vArr[1], vArr[2], vArr[3], vArr[4], pars)
	}

	level++
	if level >= len(condArr) {
		return node.ID, node
	}
	id, node := addEpisodicNodesToBrange(node, level, condArr, newPARAMS)
	return id, node
}

///////////////////////////////////////

/*
	поиск конечного узла ветки (lastBrangeID) дерева (root - ID начального узла == 0) по массиву ID узлов ветки (cond []int)

Поиск начинается с первого узла (по значению cond[0]).
Если первый узел найден, то findSampleBrange вызывается рекурсивно для поиска следующего узла
и так далее, пока не будет найден удел последнего члена cond.
Если для текущего cond[level] узел в редеве не существует, то в дереве доращивается ветка по значениям cond []int.

Возвращает ID последней найденной ветки и ее уровень, если конечный узел не найден (если найден, то len(cond)-1).
Если вернула уровень меньше, чем максимальное число уровней ветки,
то ветка доращивается func addNodesToBrange

Ищет до узла Action включительно, а к этому узлу могут быть прикреплены несколько PARAMS
*/
func findEpisodicBrange(level int, cond []int, root *EpisodicTreeNode) (int, int) {

	// Обработка случая когда мы достигли конца дерева
	if cond == nil || len(cond) <= 0 || level >= len(cond) {
		return root.ID, len(cond)
	}

	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children {
		if isEquivalentCondition(level, &child, cond) {
			// Если узел найден, продолжим рекурсивно искать в нем далее
			id, lev := findEpisodicBrange(level+1, cond, &child)
			if id == 0 {
				/*Eсли условие cond []int содержит значение 0 для какого-то уровня, то на этом уровне поиск заканчивается.
				if cond[lev]==0{// считается нормальный поиск по неполному условию

				}*/
				return child.ID, lev // поиск закончен на child.ID
				//return 0,lev // не найдено совпадение на данном уровне
			}

			return id, lev
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return 0, level
}

// //////////////////////////////////////////////////
func isEquivalentCondition(level int, node *EpisodicTreeNode, cond []int) bool {
	// массив значений по уровням - в зависимости от числа уровней в ветке, прописывается вручную
	nArr := []int{node.BaseID, node.EmotionID, node.NodePID, node.Trigger, node.Action}

	for i := 0; i < len(cond) && i <= level; i++ {
		if cond[i] != nArr[i] {
			return false
		}
	}
	return true
}

/////////////////////////////////////////////////////////

/* загрузить записанное дерево
 */
func loadEpisodicTree() {
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/episodic_tree.txt")
	initEpisodicTree(strArr)

	str := lib.ReadFileContent(lib.GetMainPathExeFile() + "/memory_psy/episodic_history.txt")

	sArr := strings.Split(str, "|")
	for n := 0; n < len(sArr); n++ {
		if len(sArr[n]) == 0 {
			break
		}
		il := strings.Split(sArr[n], ",")
		id, _ := strconv.Atoi(il[0])
		lifeTime, _ := strconv.Atoi(il[1])
		EpisodicHistoryArr = append(EpisodicHistoryArr, History{id, lifeTime})
	}

	// ОТЛАДКА, ТЕСТИРОВАНИЕ
	// saveNewEpisodic(67,38,107,1,3,3)
	//	saveNewEpisodic(200,39,100,1,3,3)

	// getEpisodesArrFromConditions(0,curStimulImageID,0)
	// getEpisodesArrFromConditions(0,1,0)
	// getEpisodesArrFromConditions(1,1,0)

	// тестирование getTargetEpisodicStrIdArr()
	testing := false
	//	testing=true
	if testing { // здесь еще не активированные деревья
		detectedActiveLastNodID = 67
		detectedActiveLastUnderstandingNodID = 38
		detectedActiveLastProblemNodID = 8
		curActiveActionsID = 1
		getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
		if targetEpisodicStrIdArr != nil {

		}
		typeRule := 1 //typeRule - 1-искать только прямые Правила, 2-искать только учительские Правила, 3- искать все виды Правил
		rule := getSingleBestRule(typeRule, curActiveActionsID)
		if rule.Action > 0 {

		}
	}
	setInterruptionEpisosde() // обязательно в конце втыкаем пустой кадр!!! Иначе после просыпания начнет дописывать новые кадры к старым цепочкам
	return
}
func initEpisodicTree(strArr []string) {

	cunt := len(strArr)
	EpisodicTreeNodeFromID = make([]*EpisodicTreeNode, cunt) // задать сразу имеющиеся в файле число при загрузке из файла
	lastIDNodeTree = 1                                       // нужно, чтобы дерево памяти началось с ID==2 (lastIDNodeTree++ ==2), иначе оно не загрузится. см ReadeEpisodicTreeNodeFromID()
	// определить нулевой узел
	//EpisodicTreeNodeFromID[0]=&EpisodicTree// все по нулям по умолчанию
	WriteEpisodicTreeNodeFromID(0, &EpisodicTree)

	//просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		if len(strArr[n]) < 2 {
			panic("Сбой загрузки дерева: [" + strconv.Itoa(n) + "] " + strArr[n])
			return
		}

		par := strings.Split(strArr[n], "#")
		p := strings.Split(par[0], "|")
		var parArr []string
		if len(par) > 1 {
			parArr = strings.Split(par[1], "|")
		}

		id, _ := strconv.Atoi(p[0])
		parentID, _ := strconv.Atoi(p[1])
		BaseID, _ := strconv.Atoi(p[2])
		EmotionID, _ := strconv.Atoi(p[3])
		NodePID, _ := strconv.Atoi(p[4])
		Trigger, _ := strconv.Atoi(p[5])
		Action, _ := strconv.Atoi(p[6])

		// считывание PARAMS
		var params []int
		if Action > 0 {
			idP, _ := strconv.Atoi(parArr[0])
			count, _ := strconv.Atoi(parArr[1])
			kind, _ := strconv.Atoi(parArr[2])
			params = []int{idP, count, kind}
		}

		parentNode, ok := ReadeEpisodicTreeNodeFromID(parentID)
		if ok {

			// Создание нового узла дерева
			node := EpisodicTreeNode{
				ID:         id,
				BaseID:     BaseID,
				EmotionID:  EmotionID,
				NodePID:    NodePID,
				Trigger:    Trigger,
				Action:     Action,
				Children:   []EpisodicTreeNode{},
				ParentID:   parentID,
				ParentNode: parentNode,
			}
			node.PARAMS = params

			node.ParentID = parentNode.ID
			node.ParentNode = parentNode
			parentNode.Children = append(parentNode.Children, node)

			// Добавление узла в массив по ID
			WriteEpisodicTreeNodeFromID(id, &node)
			lastIDNodeTree++

			// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в EpisodicTreeNodeFromID:
			// ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
			updatingEpisodicTreeNodeFromID(parentNode) // здесь потому, что при загрузке из файла нужно на лету получать адреса
		}
		/*
			// новый узел с каждой строкой из файла
			//parent,ok:=EpisodicTreeNodeFromID[parentID]
			parent,ok:=ReadeEpisodicTreeNodeFromID(parentID)
			if ok {
				// false - не проверять наличие ветки т.к. еще нет веток, они только глузятся
				id,newNodw:=createEpisodicNodeTree(parent, id, BaseID, EmotionID, NodePID, Trigger, Action, params)
				if id>0 && newNodw!=nil{

					if id>0{
						id=id
					}
				}
			}*/
	}
	return
}

/////////////////////////////////////

func SaveEpisodicTree() {
	var out = ""
	cnt := len(EpisodicTree.Children)
	for n := 0; n < cnt; n++ { // чтобы записывалось по порядку родителей
		out += getEpisodicTreeNode(&EpisodicTree.Children[n])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/episodic_tree.txt", out)

	// запись стории эпизодов EpisodicHistoryArr []int
	out = ""
	for n := 0; n < len(EpisodicHistoryArr); n++ {
		out += strconv.Itoa(EpisodicHistoryArr[n].ID) + "," + strconv.Itoa(EpisodicHistoryArr[n].LifeTime) + "|"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/episodic_history.txt", out)

	return
}
func getEpisodicTreeNode(wt *EpisodicTreeNode) string {
	var out = ""
	//	if wt.ParentID>0 {
	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|"
	out += strconv.Itoa(wt.BaseID) + "|"
	out += strconv.Itoa(wt.EmotionID) + "|"
	out += strconv.Itoa(wt.NodePID) + "|"
	out += strconv.Itoa(wt.Trigger) + "|"
	out += strconv.Itoa(wt.Action) //+ "#"

	// записать массивы PARAMS
	parsArr := getEpisodicPARAMS(wt.ID)
	if parsArr != nil {
		out += "#"
		out += strconv.Itoa(parsArr[0]) + "|"
		out += strconv.Itoa(parsArr[1]) + "|"
		out += strconv.Itoa(parsArr[2]) + "|"
	}

	out += "\r\n"
	//	}
	if wt.Children == nil { // конец
		return out
	}
	for n := 0; n < len(wt.Children); n++ {
		out += getEpisodicTreeNode(&wt.Children[n])
	}
	return out
}

/////////////////////////////////////

// Очистить эпизодическую память
func ClianEpisodicMemory() {
	lib.WriteFileContentExactly(lib.GetMainPathExeFile()+"/memory_psy/episodic_tree.txt", "")
	lib.WriteFileContentExactly(lib.GetMainPathExeFile()+"/memory_psy/episodic_history.txt", "")

	EpisodicHistoryArr = nil
	lastIDNodeTree = 1 // нельзя 0, не сработает ReadeEpisodicTreeNodeFromID()!!!
	WriteEpisodicTreeNodeFromID(0, &EpisodicTree)
}

/////////////////////////////////////

/*
	вставить кадр прерывания цепочки (темы)

Означает, что цепочка кадров памяти данной темы закончиена (fornit.ru/67675).
Вставляется по щелчку по плащке ожидания ответа (func StopWaitingWeriodFromOperator())
TODO вставлять при смене темы и произвольно.
*/
func setInterruptionEpisosde() {
	// первым не включать
	if len(EpisodicHistoryArr) == 0 {
		return
	}

	// если только что уже был пустой кадр, то не вставлять лишний
	lastI := len(EpisodicHistoryArr) - 1
	if EpisodicHistoryArr[lastI].ID == -1 {
		return
	}
	// пустой кадр с ID=-1
	EpisodicHistoryArr = append(EpisodicHistoryArr, History{-1, LifeTime})
}

///////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/episodic_memory_tree_functions.go
____________________________________________
/*  функции Дерева эпизодической памяти

TODO
Кроме работы с кадрами памяти для 1 и 2 уровней осмывсления, где все дается функциями в готовом виде
и поэтому очень механично, нужно ввести инфо-функции более детального рассмотрения цадров с выводами.

*/

package psychic

import (
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
)

///////////////////////////////////////////////////////////////////////

/*
Найти все ID узлов EpisodicTreeNode для которых выполняется условие с любым эффектом.

level == 0 - для всех трех условий (NodeAID,NodeSID,NodePID)
level == 1 - для двух первых условий (NodeAID,NodeSID)
level == 2 - только для NodeAID

Значение Trigger определеяется обязательно, обычно это curStimulImageID
Значение Action может быть ==0 и тогда результат покажет возможные варианты ответов на Trigger
Если определены Trigger и Action, то результат оказывается предсказанием того, какой эффект будет при Action.

- ДЛЯ ТЕКУЩИХ СТИМУЛА (Trigger) Ответ (Action) не определен - его и нужно найти подходящий

т.к. пишется как прямое так и учительское правило, то можно подставлять Ответ в Стимул (Trigger int)

typeRule - 1-искать только прямые Правила, 2-искать только учительские Правила, 3- искать все виды Правил
*/
var ePsrsIdArr []int

func getEpisodesArrFromConditions(typeRule int, level int, Trigger int, Action int) []int {
	if Trigger == 0 {
		if !wasRunProvocationFunc { // для func infoFunc31 Trigger равен 0
			return nil
		}
	}

	ePsrsIdArr = nil

	switch level {
	case 0:
		nodePID := detectedActiveLastProblemNodID
		if nodePID == 0 { // еще нет detectedActiveLastProblemNodID
			return nil
		}
		cond := []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID, nodePID}
		//	 cond= []int{67,38,107,1,}; //Action=3
		id, _ := findEpisodicBrange(0, cond, &EpisodicTree) // ищет ветку, которая полностью соотвествует условиям
		if id == 0 {
			return nil
		}
		node, ok := ReadeEpisodicTreeNodeFromID(id)
		if !ok {
			return nil
		}
		// если в ветке нет одного из условий, то - не найдено
		if node.BaseID != cond[0] || node.EmotionID != cond[1] || node.NodePID != cond[2] {
			return nil
		}
		getIdArr(typeRule, node, Trigger, Action)
		return ePsrsIdArr

	case 1:
		cond := []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID} // нужно выделять только те, у которых Trigger==curStimulImageID
		//cond= []int{67,38,}; Trigger=1; Action=3
		id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
		if id == 0 {
			return nil
		}
		//node, ok := EpisodicTreeNodeFromID[id]
		node, ok := ReadeEpisodicTreeNodeFromID(id)
		if !ok {
			return nil
		}
		if node.BaseID != cond[0] || node.EmotionID != cond[1] {
			return nil
		}
		getIdArr(typeRule, node, Trigger, Action)
		return ePsrsIdArr

	case 2:
		cond := []int{CurrentCommonBadNormalWell} // нужно выделять только те, у которых Trigger==curStimulImageID
		//cond= []int{67,38,}; Trigger=1; Action=3
		id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
		if id == 0 {
			return nil
		}
		//node, ok := EpisodicTreeNodeFromID[id]
		node, ok := ReadeEpisodicTreeNodeFromID(id)
		if !ok || node.BaseID != cond[0] {
			// найти при любых условиях по всему дереву
			getIdArr(typeRule, &EpisodicTree, Trigger, Action)
			return ePsrsIdArr
		}

		getIdArr(typeRule, node, Trigger, Action)
		return ePsrsIdArr
	}

	return nil
}

/*
	вытащить все ID узлов, в которых есть условие, начиная с node

еще есть
func getAllChidsFromNode(rt *EpisodicTreeNode) -начиная с узла дерева собрать все конечные узлы рекурсивно в episodiCadresArr[]
*/
func getIdArr(typeRule int, node *EpisodicTreeNode, Trigger int, Action int) {
	for _, child := range node.Children {

		if child.PARAMS != nil { // узел с прописанным PARAMS
			if Trigger > 0 && child.Trigger != Trigger {
				continue
			}
			if Action > 0 && child.Action != Action {
				continue
			}
			if typeRule == 1 && isTeacherRuleNode(&child) {
				continue
			}
			if typeRule == 2 && !isTeacherRuleNode(&child) {
				continue
			}
			if child.ID == -1 {
				continue
			}

			ePsrsIdArr = append(ePsrsIdArr, child.ID)
		}
		// продолжим рекурсивно искать в нем далее
		getIdArr(typeRule, &child, Trigger, Action)
	}

	return
}

////////////////////////////////////////////

/*
	Поиск ответного действия по Правилам, принцип GPT: fornit.ru/66139.

Здесь не учитываются учительские правила чтобы цепочки были только из прямых правил
Получить приемлемую последовательность id EpisodicTreeNode от данного Стимула Trigger
в виде глобального массива targetEpisodicStrIdArr цепочек Правил.
Назовет ее целевой последовательностью, т.е. если на данный Trigger отвечать targetEpisodicStrIdArr[0]
а опертор ответит EpisodicTreeNodeFromID[targetEpisodicStrIdArr[1]].Trigger и т.д.
то придем к тому позитивному результату, который прогнозировался при получении targetStrIdArr.
Если оператор ответит непредсказуемо, то нужно снова вызвать getTargetEpisodicStrIdArr(EpisodicTreeNodeFromID[targetEpisodicStrIdArr[1]].Trigger)
и получить скорректированную последовательность.

targetStrIdArr состояит из зафиксированных ранее непрерывных цепочек в EpisodicHistoryArr,
между которыми пустой кадр

limit int - исторические цепочки не более чем в limit эпизодов
При поиске избегать узлов пустых кадров.

В конце найденных цепочек должно быть позитивное Правило, так их можно сравнивать по эффектам и выбрать лучшую.

Получаем цепочку (или одно правило), заканчивающееся позитивом.

	В цепочке только последний член кончается удачей, остальные отрицательные,
	но смысл цепочки в том, что они приводит к удаче и поеэтому в данных конкретных условиях
	следует выполнить первую акций из этой цепочки.

Пример вызова:
getTargetEpisodicStrIdArr(3,curActiveActionsID, 5)

	//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
	//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
	if targetEpisodicStrIdArr != nil {
		// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
		rules = targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
	}
	if rules.Action > 0 {
		res := makeActionFromRooles(rules) //	!!!!!ПЕРЕДЕЛАТЬ для type Rule struct {
		return res
	}
*/
var targetEpisodicStrIdArr []Rule // окончательно выбранная целевая цепочка Правил. Всегда есть.
// из цепочки Правил с конечным плюсовым эффектом можно вытащить следующее действие на исполнение.
var episodicStrIdArr [][]Rule // несколько найденных целевых цепочек Правил. Может не быть массива альтернативных цепочек.

func getTargetEpisodicStrIdArr(Trigger int, limit int) {

	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil

	// попытка найти в точном соотвествии с условиями
	nArr := getEpisodesArrFromConditions(1, 0, Trigger, 0)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 1, Trigger, 0)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 2, Trigger, 0)
	}
	if nArr == nil { // нет никакого опыта с Trigger
		return
	}
	/////////////////////////////////////////

	/* искать цепочки подряд идущих эпизодов, начинающиеся с данного nArr[i],
	  кончающиеся хорошим эффектом и собирать их targetEpisodicStrIdArr[]
	Значение limit пока ==5 но может оптимизироваться (TODO)
	*/
	getPositiveChainsFromPositivArs(1, nArr)
	if targetEpisodicStrIdArr != nil {
		return
	}
	return
}

//////////////////////////////////////////////////////////////////

/*
	Выбрать лучшее Правило, как можно с более точным учетом условий НО БЕЗ СТИМУЛА

для провокации func infoFunc31
ДОЛЖНО БЫТЬ wasRunProvocationFunc = true
*/
func getProvocationBestRule() Rule {
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil
	rule := Rule{0, 0, 0, 0, 0}
	if !wasRunProvocationFunc {
		return rule
	}

	// попытка найти в точном соотвествии с условиями
	nArr := getEpisodesArrFromConditions(3, 0, 0, 0)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(3, 1, 0, 0)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(3, 2, 0, 0)
	}
	if nArr == nil { // нет никакого опыта с Trigger
		return rule
	}
	// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
	ruleFromEpisodeIdArr(nArr, 1)
	// Выбрать одно лучшее Правило
	_, rule = findBestRule(targetEpisodicStrIdArr)

	return rule
}

////////////////////////////////////////////////////////////////////

/*
	Выбрать лучшее Правило, как можно с более точным учетом условий

typeRule - 1-искать только прямые Правила, 2-искать только учительские Правила, 3- искать все виды Правил
результат - все найденные Правила в targetEpisodicStrIdArr
и возвращает лучшее Правило
*/
func getSingleBestRule(typeRule, Trigger int) Rule {

	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil
	rule := Rule{0, 0, 0, 0, 0}

	// попытка найти в точном соотвествии с условиями
	nArr := getEpisodesArrFromConditions(typeRule, 0, Trigger, 0)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(typeRule, 1, Trigger, 0)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(typeRule, 2, Trigger, 0)
	}
	if nArr == nil { // нет никакого опыта с Trigger
		return rule
	}
	// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
	ruleFromEpisodeIdArr(nArr, 1)
	// Выбрать одно лучшее Правило
	_, rule = findBestRule(targetEpisodicStrIdArr)

	return rule
}

////////////////////////////////////////////////////////////////////

/*
	При оценке возможности запуска автоматизма

посмотреть в Правилах, м.б. после плохого эффекта последует следующее Правило с хорошим эффектом
(не только для GPT, а при typeRule==3 - любые Правила)
и тогда можно допустить Usefulness<0 в расчете на последующий успех.
Не в качестве волевого усилия, а чисто автоматически использовать такую информацию.

Для точного соблюдения условий 3-х деревьев.

Смотрим, было ли раньше, что после данного ответа Action получилось удачно,
причем не в каком-то одном случае, а в целом статистика.

GPT. ЗДЕСЬ не смотрится строго последовательность правил.

typeRule - 1-искать только прямые Правила, 2-искать только учительские Правила, 3- искать все виды Правил
*/
func isNextWellEffectFromActonRules(typeRule int, Trigger int, Action int) bool {
	/* Найти все ID узлов EpisodicTreeNode для которых выполняется условия 3-х деревьев
	между которыми есть пустой кадр
	*/
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil
	episNodes := getEpisodesArrFromConditions(typeRule, 0, Trigger, Action)
	/* смотреть всегда точно !!!
	if episNodes==nil{// исключаем из условий 3-е дерево
		episNodes=getEpisodesArrFromConditions(typeRule,1,Trigger,Action)
	}
	if episNodes==nil{// исключаем из условий 2-е и 3-е дерево
		episNodes=getEpisodesArrFromConditions(typeRule,2,Trigger,Action)
	}*/

	/* искать цепочки подряд идущих эпизодов, начинающиеся с данного nArr[i],
	  кончающиеся хорошим эффектом и собирать их targetEpisodicStrIdArr[]
	Значение limit пока ==5 но может оптимизироваться (TODO)
	*/
	getPositiveChainsFromPositivArs(typeRule, episNodes)
	if targetEpisodicStrIdArr != nil {
		return true
	}

	/*
		if episNodes == nil {
			return false
		}
		for i := 0; i < len(episNodes); i++ {
			node, ok := ReadeEpisodicTreeNodeFromID(episNodes[i])
			if !ok {
				continue
			}
			effect:=node.PARAMS[0]
				if effect==100{effect=1}
				if effect > 0 { // [хоть один вариант позитивный
					return true
				}
		}*/
	return false
}

/////////////////////////////////////////////////////////////////

/*
	выдать []Rule для данного ID фразы с позитивным Эфектом

чтобы из полученных действий составить цепочку для автоматизма
exactlyCond - true - правила для текущих условий, false - только для 1-го дерева
*/
func getWellRoolesFromPhraseId(phraseID int, exactlyCond bool) []Rule {
	var rArr []Rule
	var cond []int
	if exactlyCond {
		cond = []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID}
	} else {
		cond = []int{CurrentCommonBadNormalWell}
	}
	/////////////////////////
	id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
	if id == 0 {
		return nil
	}
	node, ok := ReadeEpisodicTreeNodeFromID(id)
	if !ok {
		return nil
	}
	// вытащить все конечные ветки, начиная с node
	getAllChidsFromNode(node)
	if episodiCadresArr == nil {
		return nil
	}

	for i := 0; i < len(episodiCadresArr); i++ {
		act, ok := ReadeActionsImageArr(episodiCadresArr[i].Trigger)
		if !ok {
			continue
		}
		if act.PhraseID[0] != phraseID {
			continue
		}
		effectCompare := 0
		params := episodiCadresArr[i].PARAMS
		effect := params[0]
		if effect == 100 {
			effect = 1
		}
		if effect > effectCompare {
			rArr = append(rArr, Rule{episodiCadresArr[i].Trigger, episodiCadresArr[i].Action, effect, params[1], 0})
			break
		}
	}

	return rArr
}

/////////////////////////////////////////////////////////////////

/*
выдать []Rule для данного ID слова с позитивным Эфектом

чтобы из полученных действий составить цепочку для автоматизма
exactlyCond - true - правила для текущих условий, false - только для 1-го дерева

ф-ция вызывается когда не распознана фраза
!!! если нераспознана фраза, то detectedActiveLastNodID НЕВЕРЕН его нельзя использовать как поиск по эп.памяти

	поэтому нужно использовать lastWellPhrasedetectedActiveLastNodID
*/
func getWellRulesFromWordId(wordID int, exactlyCond bool) []Rule {
	var rArr []Rule
	var cond []int
	if exactlyCond {
		cond = []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID}
	} else {
		cond = []int{CurrentCommonBadNormalWell}
	}
	/////////////////////////
	id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
	if id == 0 {
		return nil
	}
	node, ok := ReadeEpisodicTreeNodeFromID(id)
	if !ok {
		return nil
	}
	// вытащить все конечные ветки, начиная с node
	getAllChidsFromNode(node)
	if episodiCadresArr == nil {
		return nil
	}

	for i := 0; i < len(episodiCadresArr); i++ {
		eArr, ok := ReadeActionsImageArr(episodiCadresArr[i].Trigger)
		if !ok {
			continue
		}
		// есть ли такое слово во фразе
		if eArr.PhraseID == nil {
			continue
		}
		aArr := word_sensor.GetWordsArrFromPraseID(eArr.PhraseID[0])

		//if act.PhraseID[0] != phraseID {
		if !lib.ExistsValInArr(aArr, wordID) { // нет такого слова во фразе
			continue
		}
		effectCompare := 0
		params := episodiCadresArr[i].PARAMS
		effect := params[0]
		if effect == 100 {
			effect = 1
		}
		if effect > effectCompare {
			rArr = append(rArr, Rule{episodiCadresArr[i].Trigger, episodiCadresArr[i].Action, effect, params[1], 0})
			//break
		}
	}

	return rArr
}

////////////////////////////////////////////////////////////////////////

/*
	Пpедсказание позитивного эффекта от Ответных действий для данных условий

Сначала ищется конец цепочки с позитивным эффектом, который превышает негативы предыдущих звеньев
и возвращает эффект конечного звена так, что действие дает позивные предсказания даже если сначала приводит к негативу.
Если таких цепочек не находится, то просто делается статистика действия после данного стимула.
Если нет такой статистики, что выдается результат статистики действия с любыми стимулами.

Возвращает точность предсказания (1-самое точное,) если найдено и величину вероятного эффекта.
Если не найдено возвращает 0,0

Отимистическое предсказание, т.е. из найденных правил выбирается наилучший жффект.
*/
func positiveFromActionAfterStimul(Trigger int, Action int) (int, int) {
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil

	// попытка найти в точном соотвествии с условиями  цепочки прямых правил с решающим позитивом на конце
	nArr := getEpisodesArrFromConditions(1, 0, Trigger, Action)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 1, Trigger, Action)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 2, Trigger, Action)
	}
	if nArr != nil { // есть кадры
		/* искать цепочки подряд идущих эпизодов, начинающиеся с данного nArr[i],
		  кончающиеся хорошим эффектом и собирать их targetEpisodicStrIdArr[]
		Значение limit пока ==5 но может оптимизироваться (TODO)
		*/
		getPositiveChainsFromPositivArs(1, nArr)
		if targetEpisodicStrIdArr != nil {
			return 1, targetEpisodicStrIdArr[0].Effect
		}
		// раз нет цепочек, учтем просто правила для условий эмоций
		// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
		ruleFromEpisodeIdArr(nArr, 1)
		// Выбрать одно лучшее Правило
		_, rule := findBestRule(targetEpisodicStrIdArr)
		return 2, rule.Effect
	}

	/* попытка найти в точном соотвествии с условиями, но не цепочки, а отдельные правила, только прямые (без учительских)
	Если не найдено для точных условий (эмоции), будет искать при любых условиях по всему дереву.
	*/
	nArr = getEpisodesArrFromConditions(1, 0, Trigger, Action)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 1, Trigger, Action)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 2, Trigger, Action)
	}
	if nArr != nil {
		// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
		ruleFromEpisodeIdArr(nArr, 1)
		// Выбрать одно лучшее Правило
		_, rule := findBestRule(targetEpisodicStrIdArr)
		return 2, rule.Effect
	}

	// искать без учета стимула
	ePsrsIdArr = nil
	getIdArr(1, &EpisodicTree, Trigger, Action)
	if ePsrsIdArr != nil {
		// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
		ruleFromEpisodeIdArr(ePsrsIdArr, 1)
		// Выбрать одно лучшее Правило
		_, rule := findBestRule(targetEpisodicStrIdArr)
		return 3, rule.Effect
	}

	return 0, 0
}

/////////////////////////////////////////////////////////////////////

/*
	Пpедсказание негативного эффекта от Ответных действий для данных условий

Пессимистическое предсказание, т.е. из найденных правил выбирается наихудший жффект.
*/
func negativeFromActionAfterStimul(Trigger int, Action int) (int, int) {
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil

	nArr := getEpisodesArrFromConditions(1, 0, Trigger, Action)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 1, Trigger, Action)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 2, Trigger, Action)
	}
	if nArr != nil {
		// собрать в targetEpisodicStrIdArr массив негативных правил типа Rule из массива кадров эпиз.памяти
		ruleFromEpisodeIdArr(nArr, 2) // 2 - массив негативных правил
		// Выбрать одно худшее Правило
		_, rule := findWorseRule(targetEpisodicStrIdArr)
		return 2, rule.Effect
	}
	return 0, 0
}

///////////////////////////////////////////////////////////////////////////

/*
	предсказание последствий выполнения действия автоматизма

для understanding_functions.go func getPrognoze(atmtzm *Automatizm)

Алгоритм:
Поиск цепочек, заканчивающихся большим негативом (превышающим позитив промежуточных)
Если не найдены цепочки, то поиск негатива в правилах с учетом эмоций и Стимула Trigger
Если нет таких, то поиск правил с участием Action

т.е. в первую очередь смотрим нет ли негатива и если нет, то автоматизм может запускаться.

Возвращает accuracy,effect:
accuracy == 1 - точное предсказание для действия, точное предсказание для действияно потом будет позитив
accuracy == 2 - менее точное предсказание для действия, совершенное после Стимула curStimulImageID в данных условиях
accuracy == 3 - неточное предсказание для действия при любом стимуле и любых условиях
*/
///////////////////////
/* СТАРАЯ ВЕРСИЯ
func getPrognoseFromAutmtzmAction(Trigger int, Action int) (int, int) {
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil

	// Все кадры, точно отвечающие условиям - в nArr[]
	nArr := getEpisodesArrFromConditions(1, 0, Trigger, Action)
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 1, Trigger, Action)
	}
	if nArr == nil {
		nArr = getEpisodesArrFromConditions(1, 2, Trigger, Action)
	}
	if nArr != nil { // есть кадры
		// искать цепочки подряд идущих эпизодов, начинающиеся с данного nArr[i],
		//кончающиеся большим негативом и собирать их targetEpisodicStrIdArr[]

targetEpisodicStrIdArr = nil
episodicStrIdArr = nil
getNegativeChainsFromPositivArs(1, nArr)
if targetEpisodicStrIdArr != nil {
return 1, targetEpisodicStrIdArr[0].Effect
} ////////////////////

// раз нет цепочек, учтем просто правила для условий эмоций
targetEpisodicStrIdArr = nil
episodicStrIdArr = nil
// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
ruleFromEpisodeIdArr(nArr, 2)
// Выбрать одно худшее Правило
_, ruleМ := findWorseRule(targetEpisodicStrIdArr)

// вдруг есть позитив
targetEpisodicStrIdArr = nil
episodicStrIdArr = nil
getPositiveChainsFromPositivArs(1, nArr)
ruleFromEpisodeIdArr(nArr, 1)
// Выбрать одно худшее Правило
_, ruleP := findBestRule(targetEpisodicStrIdArr)
if ruleP.Effect > ruleМ.Effect {
return 2, ruleP.Effect
}
return 2, ruleМ.Effect
}

// искать без учета стимула
targetEpisodicStrIdArr = nil
episodicStrIdArr = nil
ePsrsIdArr = nil
getIdArr(1, &EpisodicTree, Trigger, Action)
if ePsrsIdArr != nil {
// собрать в targetEpisodicStrIdArr массив негативных правил типа Rule из массива кадров эпиз.памяти
ruleFromEpisodeIdArr(nArr, 2) // 2 - массив негативных правил
// Выбрать одно лучшее Правило
_, ruleМ := findWorseRule(targetEpisodicStrIdArr)

// вдруг есть позитив
targetEpisodicStrIdArr = nil
episodicStrIdArr = nil
getPositiveChainsFromPositivArs(1, nArr)
ruleFromEpisodeIdArr(nArr, 1)
// Выбрать одно худшее Правило
_, ruleP := findBestRule(targetEpisodicStrIdArr)
if ruleP.Effect > ruleМ.Effect {
return 2, ruleP.Effect // 2 т.к. найдено в цепочках
}
return 3, ruleМ.Effect
}
return 0, 0
}
*/

/*
	предсказание последствий выполнения действия автоматизма

для understanding_functions.go func getPrognoze(atmtzm *Automatizm)

Алгоритм:
Поиск цепочек, заканчивающихся большим негативом (превышающим позитив промежуточных)
Если не найдены цепочки, то поиск негатива в правилах с учетом эмоций и Стимула Trigger
Если нет таких, то поиск правил с участием Action

т.е. в первую очередь смотрим нет ли негатива и если нет, то автоматизм может запускаться.

Возвращает accuracy,effect:
accuracy == 1 - точное предсказание для действия, точное предсказание для действияно потом будет позитив
accuracy == 2 - менее точное предсказание для действия, совершенное после Стимула curStimulImageID в данных условиях
accuracy == 3 - неточное предсказание для действия при любом стимуле и любых условиях
*/
func getPrognoseFromAutmtzmAction(Trigger int, Action int) (int, int) {
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil

	// Все кадры, точно отвечающие условиям - в nArr[]
	nArr := getEpisodesArrFromConditions(1, 0, Trigger, Action)
	if nArr != nil {
		ef := finalCommonResult(nArr)
		return 1, ef
	}
	nArr = getEpisodesArrFromConditions(1, 1, Trigger, Action)
	if nArr != nil {
		ef := finalCommonResult(nArr)
		return 2, ef
	}
	nArr = getEpisodesArrFromConditions(1, 2, Trigger, Action)
	if nArr != nil { // есть кадры
		ef := finalCommonResult(nArr)
		return 3, ef
	} ////////////////////

	// искать без учета стимула
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil
	ePsrsIdArr = nil
	getIdArr(1, &EpisodicTree, Trigger, Action)
	if ePsrsIdArr != nil {
		ef := finalCommonResult(nArr)
		return 3, ef
	}
	return 0, 0
}

// вернет положительное или отрицательное значение эффекта
func finalCommonResult(nArr []int) int {
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil
	// собрать в targetEpisodicStrIdArr массив позитивных правил типа Rule из массива кадров эпиз.памяти
	ruleFromEpisodeIdArr(nArr, 2)
	// Выбрать одно худшее Правило
	_, ruleМ := findWorseRule(targetEpisodicStrIdArr)

	// вдруг есть позитив
	targetEpisodicStrIdArr = nil
	episodicStrIdArr = nil
	getPositiveChainsFromPositivArs(1, nArr)
	ruleFromEpisodeIdArr(nArr, 1)
	// Выбрать одно лучшее Правило
	_, ruleP := findBestRule(targetEpisodicStrIdArr)

	if ruleP.Effect > ruleМ.Effect {
		return ruleP.Effect
	}
	return ruleМ.Effect

	//return ruleP.Effect, ruleМ.Effect
}

///////////////////////////////////////////////////////////

/*
выдать все последующие кадры в цепочках, после данного (memFr *EpisodicTreeNode)
до пустых кадров (ID==-1)
*/
func getNextHistiryEpisodicFrame(memFr *EpisodicTreeNode) []*EpisodicTreeNode {
	if len(EpisodicHistoryArr) == 0 {
		return nil
	}
	startID := memFr.ID
	var out []*EpisodicTreeNode
	for n := 0; n < len(EpisodicHistoryArr)-2; n++ {
		if EpisodicHistoryArr[n].ID == startID && EpisodicHistoryArr[n+1].ID != -1 {
			frm, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[n+1].ID)
			if ok {
				out = append(out, frm)
			}
		}
	}

	return out
}

____________________________________________

BOT/brain/psychic/episodic_memory_tree_functions_utilites.go
____________________________________________
/* Вспомогательные функции

 */

package psychic

///////////////////////////////////////////////////////////////////////////////

// сила эффекта правил PARAMS[0] в зависимости от count PARAMS[1]
func getWpower(effect int, count int) int {
	if count < 3 {
		return effect
	}
	if count < 6 {
		return effect * 2
	}
	return effect * 3
}

// сила значимости стимула PARAMS[2] в зависимости от count PARAMS[1]
func getOpower(stimulsEffect int, count int) int {
	if count < 3 {
		return stimulsEffect
	}
	if count < 6 {
		return stimulsEffect * 2
	}
	return stimulsEffect * 3
}

func getLimitCountEM() int {
	limitN := 20
	if EvolushnStage < 4 {
		limitN = 2
	}
	if EvolushnStage == 4 {
		limitN = 5
	}
	return limitN
}

//////////////////////////////////////////////////

// функция, находящая член массива ruleArr с максимально высоким сочетанием Effect и Count
func findBestRule(ruleArr []Rule) (int, Rule) {
	if ruleArr == nil {
		return -1, Rule{0, 0, 0, 0, 0}
	}
	maxEffectCount := -1 // чтобы нулевые эффекты тоже учитывались
	var bestRule Rule
	id := 0
	for idr, rule := range ruleArr {
		wEffect := getWpower(rule.Effect, rule.Count)
		if wEffect > maxEffectCount {
			maxEffectCount = wEffect
			bestRule = rule
			id = idr
		}
	}
	return id, bestRule
}

// /////////////////////////////////////////////////////
// функция, находящая член массива ruleArr с максимально низким сочетанием Effect и Count
func findWorseRule(ruleArr []Rule) (int, Rule) {
	if ruleArr == nil {
		return -1, Rule{0, 0, 0, 0, 0}
	}
	maxEffectCount := 0 // нулевые эффекты - не вредны
	var bestRule Rule
	id := 0
	for idr, rule := range ruleArr {
		wEffect := getWpower(rule.Effect, rule.Count)
		if wEffect < maxEffectCount {
			maxEffectCount = wEffect
			bestRule = rule
			id = idr
		}
	}
	return id, bestRule
}

///////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/* Вытащить из эпизод.памяти посленюю цепочку кадров по числу limit
чтобы использовать ее как шаблон для поиска правил.
Не стоит задавать limit больше 5-10 (оптимизируется)
*/
func getLastSequenceFromEpisodeMemory(limit int) []int {
	ln := len(EpisodicHistoryArr)
	if ln < limit {
		limit = ln - 1
	}
	ip := EpisodicHistoryArr[(ln - limit):]
	var out []int
	for n := 0; n < len(ip); n++ {
		out = append(out, ip[n].ID)
	}
	return out
}

/////////////////////////////////////////////////////////////////

// последний индекс массива EpisodicHistoryArr []int Если
func getEpisodeFromLastMemory(from int) int {
	ln := len(EpisodicHistoryArr)
	if ln == 0 {
		return 0
	}
	return ln - 1
}

//////////////////////////////////////////

// это кадр с учительским правилом
func isTeacherRuleNode(node *EpisodicTreeNode) bool {
	if node.PARAMS[0] == 100 {
		return true
	}
	return false
}

//////////////////////////////////////////////////////////////////

/*
	собрать в targetEpisodicStrIdArr массив только позитивных правил типа Rule из массива кадров эпиз.памяти

kind 0 - любой эффект, 1 - только позитивные правила, 2 - только негативные правила
*/
func ruleFromEpisodeIdArr(nArr []int, kind int) {
	targetEpisodicStrIdArr = nil
	for i := 0; i < len(nArr); i++ {
		node, ok := ReadeEpisodicTreeNodeFromID(nArr[i])
		if !ok {
			continue
		}
		pars := node.PARAMS
		if pars == nil {
			continue
		}

		effect := pars[0]
		if effect == 100 {
			effect = 1
		}
		if kind == 1 && effect < 0 {
			continue
		}
		if kind == 2 && effect >= 0 {
			continue
		}
		// добавить Правило
		rule := Rule{node.Trigger, node.Action, effect, pars[1], 0}
		targetEpisodicStrIdArr = append(targetEpisodicStrIdArr, rule)
	}
	return
}

/////////////////////////////////////////////////////////////////////////////

// собрать в targetEpisodicStrIdArr массив только негативных правил типа Rule из массива кадров эпиз.памяти
func ruleNegativeFromEpisodeIdArr(nArr []int) {
	targetEpisodicStrIdArr = nil
	for i := 0; i < len(nArr); i++ {
		node, ok := ReadeEpisodicTreeNodeFromID(nArr[i])
		if !ok {
			continue
		}
		pars := node.PARAMS
		if pars == nil {
			continue
		}

		effect := pars[0]
		//		if effect==100{effect=1}
		if effect < 0 {
			// добавить Правило
			rule := Rule{node.Trigger, node.Action, pars[0], pars[1], 0}
			targetEpisodicStrIdArr = append(targetEpisodicStrIdArr, rule)
		}
	}
	return
}

/////////////////////////////////////////////////////////////////////////////

/*
	искать цепочки подряд идущих эпизодов, начинающиеся с данного nArr[i],
	 кончающиеся хорошим эффектом и собирать их targetEpisodicStrIdArr[]

заранее должно быть уже обнулено:
targetEpisodicStrIdArr=nil
episodicStrIdArr=nil

typeRule - 1 - искать только прямые Правила, 2 - искать только учительские Правила, 3 - искать все виды Правил
*/
func getPositiveChainsFromPositivArs(typeRule int, nArr []int) {
	//тут скорей всего надо делать перебор с конца, чтобы искать сначала в самой свежей цепочке
	for i := 0; i < len(nArr); i++ {
		// с позитивным концом
		flgBreak := getEpisodicStrIdArr(typeRule, 1, nArr[i], getLimitCountEM())
		if flgBreak {
			// ищем строго в одной цепочке, чтобы не оборвать смысловую (причинно-следственную связь) при поиске.
			// Здесь (2 уровень сознания) причина-следствие определяется тупо по принципу принадлежности кадров памяти одной цепочке:
			// если не было обрыва времени ожидания (внимания) - значит все как то связано, а если был обрыв - это уже другое событие
			// связь  между цепочками или кадрами разных цепочек установливается произвольно на следующих уровнях мышления
			break
		}
	}

	if episodicStrIdArr != nil {
		if len(episodicStrIdArr) > 1 { // нашли несколько альтернативных цепочек, нужно выбрать лучшую из episodicStrIdArr
			// сравниваем Правила и выбираем лучшее по эффекту и уверенности
			var ruleArr []Rule
			for n := 0; n < len(episodicStrIdArr); n++ {
				curArr := episodicStrIdArr[n]
				last := curArr[len(curArr)-1]
				ruleArr = append(ruleArr, last)
			}
			_, rule := findBestRule(ruleArr)
			if rule.Effect >= 0 {
				targetEpisodicStrIdArr = []Rule{rule}
			}

		} else { // единственная
			targetEpisodicStrIdArr = append(targetEpisodicStrIdArr, episodicStrIdArr[0][0])
		}
	}
}

//////////////////////////////////////////////////////////////////

/*
	искать цепочки подряд идущих эпизодов, начинающиеся с данного nArr[i],

кончающиеся большим негативом и собирать их targetEpisodicStrIdArr[]

заранее должно быть уже обнулено:
targetEpisodicStrIdArr=nil
episodicStrIdArr=nil
*/
func getNegativeChainsFromPositivArs(typeRule int, nArr []int) {

	for i := 0; i < len(nArr); i++ {
		//искать цепочки с негативным концом
		flgBreak := getEpisodicStrIdArr(typeRule, 2, nArr[i], getLimitCountEM())
		if flgBreak {
			// ищем строго в одной цепочке, чтобы не оборвать смысловую (причинно-следственную связь) при поиске.
			// Здесь (2 уровень сознания) причина-следствие определяется тупо по принципу принадлежности кадров памяти одной цепочке:
			// если не было обрыва времени ожидания (внимания) - значит все как то связано, а если был обрыв - это уже другое событие, никак не связанное с предыдущим
			// связь  между цепочками или кадрами разных цепочек установливается произвольно на следующих уровнях мышления
			break
		}
	}

	if episodicStrIdArr != nil {
		if len(episodicStrIdArr) > 1 { // нашли несколько альтернативных цепочек, нужно выбрать лучшую из episodicStrIdArr
			// сравниваем Правила и выбираем лучшее по эффекту и уверенности
			var ruleArr []Rule
			for n := 0; n < len(episodicStrIdArr); n++ {
				curArr := episodicStrIdArr[n]
				last := curArr[len(curArr)-1]
				ruleArr = append(ruleArr, last)
			}
			_, rule := findWorseRule(ruleArr)
			if rule.Effect >= 0 {
				targetEpisodicStrIdArr = []Rule{rule}
			}

		} else { // единственная
			targetEpisodicStrIdArr = append(targetEpisodicStrIdArr, episodicStrIdArr[0][len(episodicStrIdArr[0])-1])
		}
	}
}

///////////////////////////////////////////

/*
	начиная с кадра ID, искать цепочку подряд идущих эпизодов исторической последовательности эпизодов EpisodicHistoryArr

начинающихся с данного nArr[i], и кончающегося пустым кадром (id=-1)
и вернуть их в массиве targetEpisodicStrIdArr[]
Находится суммарные негативные и позитивные эффекты в цепочке.
При typeEffect==1 собираются только цепочки с превышающим позитивным ээфектом.
При typeEffect==2 собираются только цепочки с превышающим негативным ээфектом.
При typeEffect==0 - любые суммарные эффекты

typeRule - 1-искать только прямые Правила, 2-искать только учительские Правила, 3- искать все виды Правил
*/
func getEpisodicStrIdArr(typeRule int, typeEffect int, id int, limit int) bool {
	if id == -1 {
		return true // не обрабатывать пустой кадр
	}
	// последний индекс исторической памяти EpisodicHistoryArr[]
	lastI := len(EpisodicHistoryArr) - 1
	var node *EpisodicTreeNode
	var strIdArr []Rule // для формирования цепочки Правил
	effectSum := 0      // максимальный эффект промежуточных звеньев
	endStr := false
	// отмотывать назад для формирования цепочек для каждого EpisodicHistoryArr[i].ID == id
	for i := lastI; i >= 0; i-- {
		// пустой кадр
		if endStr {
			break // конец цепочки, начало следующей  для кадра id
		}
		// как только наткнулись на кадр с целевым id смотрим для него цепочку
		if EpisodicHistoryArr[i].ID == id {
			// начиная с i посмотреть что там дальше и сформировать цепочку
			for n := i; n < lastI || ((n-i) < limit && n < lastI); n++ { //во второй части условия нужно учесть n < lastI, иначе периодически выдает выход за пределы индексов массива
				if EpisodicHistoryArr[n].ID == -1 {
					endStr = true
					break
				}

				//				node=EpisodicTreeNodeFromID[EpisodicHistoryArr[i]]
				node0, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[n].ID)
				if !ok {
					continue
				}
				node = node0

				pars := node.PARAMS
				if pars == nil {
					continue
				}

				effect := pars[0]
				count := pars[1]
				if typeRule == 1 && effect == 100 {
					continue
				} // не учитывать учительскаие правила
				if typeRule == 2 && effect != 100 {
					continue
				}

				effectSum += getWpower(effect, count)

				// добавить Правило
				rule := Rule{node.Trigger, node.Action, pars[0], pars[1], 0}
				strIdArr = append(strIdArr, rule)
			} //for n := i; n < lastI || (n-i) < limit; n++ {
			canAddString := false
			if strIdArr != nil && len(strIdArr) > 0 {
				if typeEffect == 1 && effectSum > 0 {
					canAddString = true
				}
				if typeEffect == 2 && effectSum < 0 {
					canAddString = true
				}
				if typeEffect == 0 {
					canAddString = true
				}
			}
			if canAddString {
				episodicStrIdArr = append(episodicStrIdArr, strIdArr)
			}
		} //if EpisodicHistoryArr[i] == id {
	} //for i := lastI; i > 0; i-- {

	return endStr
}

////////////////////////////////////////////////////////////////////

// найти ID исторической памяти для эпизода с акциями Trigger или Action, начиная с номера beginID
func getFrameFromTrigger(beginID int, actID int) int {

	for i := beginID; i < len(EpisodicHistoryArr); i++ {

		node, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[i].ID)
		if !ok {
			continue
		}
		if node.PARAMS == nil { // на всякий случай, хотя такого не должно быть
			continue
		}
		if node.Trigger == actID || node.Action == actID {
			return i
		}

	}
	return 0
}

////////////////////////////////////////////////

// от данного ID исторической памяти пройти назад до пустого кадра и вернуть его ID
func returnEmptyHistoryID(beginID int) int {
	for i := beginID; i >= 0 && i > 0; i-- {

		node, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[i].ID)
		if !ok {
			continue
		}
		if node.PARAMS == nil { // на всякий случай, хотя такого не должно быть
			continue
		}
		if node.Action == 0 {
			return i
		}
	}
	return 0
}

//////////////////////////////////////////////////

/*
начиная с узла дерева собрать все конечные узлы рекурсивно в episodiCadresArr[]

еще есть
func getIdArr(typeRule int, node *EpisodicTreeNode, Trigger int, Action int) - вытащить все ID узлов, в которых есть условие, начиная с node
*/
var episodiCadresArr []*EpisodicTreeNode

func getAllChidsFromNode(rt *EpisodicTreeNode) {
	episodiCadresArr = nil
	getAllChidsFromNodeProc(rt)
}
func getAllChidsFromNodeProc(rt *EpisodicTreeNode) {
	if len(rt.Children) == 0 { // конец ветки
		episodiCadresArr = append(episodiCadresArr, rt)
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		getAllChidsFromNodeProc(&rt.Children[i]) // рекурсия на слудующие дочки
	}
}

//////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/episodic_mental_memory_funcs.go
____________________________________________
/* функции для работы в сентальными эпизодами памяти

В отличие от моторной эпиз.памяти здесь нет цепочек правил, разделенных пустыми кадрами.
Здесь в каждом кадре есть цепочка инфо-функций, которые применялись в данных условиях.
Тут принцип GPT - в том, что после запуска инфо-фукнции прогнозируется запуск следующей с определенной уверенностью.

В принципе здесь и не нужна оказывается историческая последовательность кадров, но она хранит время.
 */

package psychic

import "strconv"

///////////////////////////////////////////////////
/* буфер результатов поиска ID кадров
 */
var ePMentSrsIdArr []int
//Не выдавать по запросу id вспомогательных функций.
func isNoWorkInfoID(id int)bool{
	if id == 1 || id == 2 || id == 5 || id == 8 || id == 14 || id == 17 || id == 26{
		return true
	}
	return false
}
///////////////////////////////////////////////////


/* Найти все ID кадров для которых выполняется условие с заданным эффектом.
NodeAID int, ThemeID int,PurposeID int могут быть ==0 НО только если есть ненулевой предыдущий,
т.е. нельзя getEpisodesMentalArrFromConditions(0, 12,0)
Тогда поиск будет максимально быстрым.
Все сразу могут быть нулевыми и тогда выдаст все кадры дерева.

Для поиска по любому из 3-х условий func getEpisodesMentalArrFromAnyConditions - более долгий перебор всех узлов дерева.

typeEffect: 0- любой, 1-позитивный, 2-негативный
*/
func getEpisodesMentalArrFromConditions(NodePID int, ThemeID int,PurposeID int,typeEffect int)[]int {

	ePMentSrsIdArr = nil

	var cond []int
	if NodePID > 0 {
		cond = append(cond, NodePID)
	}
	if ThemeID > 0 {
		cond = append(cond, ThemeID)
	}
	if PurposeID > 0 {
		cond = append(cond, PurposeID)
	}

	id, _ := findEpisodicMentalBrange(0, cond, &EpisodicMentalTree)
	if id == 0 {
		return nil
	}

	//		node, ok := EpisodicTreeNodeFromID[id]
	node, ok := ReadeEpisodicMentalTreeNodeFromID(id)
	if !ok {
		return nil
	}
	getMentalIdArr(node,typeEffect)
	return ePMentSrsIdArr

	return nil
}
// вытащить все ID узлов, в которых есть условие, начиная с node
func getMentalIdArr(node *EpisodicMentalTreeNode,typeEffect int){
	for _, child := range node.Children {

		if child.PARAMS !=nil { // узел с прописанным PARAMS и там есть InfoArr
			if typeEffect==1 && child.PARAMS[1]<0{
				continue
			}
			if typeEffect==2 && child.PARAMS[1]>=0{
				continue
			}
			if isNoWorkInfoID(child.ID){
				continue
			}
			ePMentSrsIdArr=append(ePMentSrsIdArr,child.ID)
		}
		// продолжим рекурсивно искать в нем далее
		getMentalIdArr(&child,typeEffect)
	}

	return
}
////////////////////////////////////////////



/*Для поиска по любому из 3-х условий func getEpisodesMentalArrFromAnyConditions - более долгий перебор всех узлов дерева.
Любой из NodeAID int, ThemeID int,PurposeID int м.б. нулем и даже все сразу
typeEffect: 0- любой, 1-позитивный, 2-негативный
*/
func getEpisodesMentalArrFromAnyConditions(NodePID int, ThemeID int,PurposeID int,typeEffect int)[]int {

	ePMentSrsIdArr = nil
	getMentalIdConditionArr(&EpisodicMentalTree,NodePID, ThemeID,PurposeID,typeEffect)
	return ePMentSrsIdArr
}
// вытащить все ID узлов, в которых есть условие, начиная с node
func getMentalIdConditionArr(node *EpisodicMentalTreeNode,NodePID int, ThemeID int,PurposeID int,typeEffect int){
	for _, child := range node.Children {

		if child.PARAMS !=nil { // узел с прописанным PARAMS и там есть InfoArr
			if NodePID>0 && child.NodePID !=NodePID{
				continue
			}
			if ThemeID>0 && child.ThemeID !=ThemeID{
				continue
			}
			if PurposeID>0 && child.PurposeID !=PurposeID{
				continue
			}
			if typeEffect==1 && child.PARAMS[1]<0{
				continue
			}
			if typeEffect==2 && child.PARAMS[1]>=0{
				continue
			}
			if isNoWorkInfoID(child.ID){continue}
			ePMentSrsIdArr=append(ePMentSrsIdArr,child.ID)
		}
		// продолжим рекурсивно искать в нем далее
		getMentalIdConditionArr(&child,NodePID, ThemeID,PurposeID,typeEffect)
	}

	return
}
////////////////////////////////////////////////////////////












/* найти ID успешной инфо-функции со все большим отклонением от текущих условий

*/
func findSuitableMentalFunc()[]int{
	NodePID:=detectedActiveLastProblemNodID // ID проблемы
	ThemeID:=problemTreeInfo.themeID
	PurposeID:=mentalInfoStruct.mentalPurposeID

	getEpisodesMentalArrFromConditions(NodePID, ThemeID,PurposeID,1)
	if ePMentSrsIdArr==nil{
		getEpisodesMentalArrFromConditions(NodePID, ThemeID,0,1)
	}
	if ePMentSrsIdArr==nil{
		getEpisodesMentalArrFromConditions(NodePID, 0,0,1)
	}
	if ePMentSrsIdArr==nil{// в поиске по всем деревьям выбираем Цель
		getEpisodesMentalArrFromAnyConditions(0, 0,PurposeID,1)
	}
	// выдавать все кадры нет смысла, так что на этом заканчиваем

	return ePMentSrsIdArr
}
////////////////////////////////////////////////////




/* Выбрать следующую финфо-функцию для запуска из цепочки наиболее подходящих для данных условий
Если еще не начато заполнение буфера исполненных функций mentAtmzmActualFuncs, то выдать первую из цепочки,
если уже выполнялись функции, дать следующую подходящую после mentAtmzmActualFuncs
GRP метод.
*/
func getNextGPTstepMental()int{
	NodePID:=detectedActiveLastProblemNodID // ID проблемы
	ThemeID:=problemTreeInfo.themeID
	PurposeID:=mentalInfoStruct.mentalPurposeID

	getEpisodesMentalArrFromConditions(NodePID, ThemeID,PurposeID,1)
	if ePMentSrsIdArr==nil{
		return 0// все на этом т.к. этот метод только для точного соотвествия условиям
	}
	// найти продолжение для mentAtmzmActualFuncs
	if mentAtmzmActualFuncs==nil{// еще не было запущено функций
		return ePMentSrsIdArr[0]
	}
	lelFound:=len(ePMentSrsIdArr)
	lenExists:=len(mentAtmzmActualFuncs)
	if lelFound < lenExists {
		return 0
	}
	isNotEquivalentFrame:=false
	for i := 0; i < lelFound && i<lenExists; i++ {
		if isNoWorkInfoID(ePMentSrsIdArr[i]){
			continue
		}
		if ePMentSrsIdArr[i] != mentAtmzmActualFuncs[i] {
			isNotEquivalentFrame = true
		}
	}
	if isNotEquivalentFrame{// нет совпадений цепочек
		return 0
	}
	lastArr:=ePMentSrsIdArr[lenExists:]
	return lastArr[0]
}
///////////////////////////////////////////////////////////////////



/* выбрать инфо-функцию для запуска в данных услових.

exactly - только точное совпадение условий
 */
func findInfoIdFromExperience(exactly bool)int{
	// по GPT
	fID:=getNextGPTstepMental()
	if fID>0{
		return fID
	}
	if exactly{
		return 0
	}
	// тогда - наиболее привычную
	fArr:=getFavoritInfoFunc()
	if fArr!=nil{
		// не выдавать 8-ю
		for i := 0; i < len(fArr); i++ {
			if !isNoWorkInfoID(fArr[i]){
				return fArr[i]
			}

		}
		return 0
	}

	return 0
}
/////////////////////////////////////////////////////////////




/* МЕНТАЛЬНЫЙ АВТОМАТИЗМ - выдать номера инфо-фукнций, привычных (Count>1) для данных условий

 */
func getFavoritInfoFunc()[]int{
	findSuitableMentalFunc()
	if ePMentSrsIdArr!=nil{
		for i := 0; i < len(ePMentSrsIdArr); i++ {
			frame,ok:=ReadeEpisodicMentalTreeNodeFromID(ePMentSrsIdArr[i])
			if ok && frame.PARAMS[1]>1{// Count>1  МОЖЕТ ОПЬТМИЗИРОВАТЬСЯ от опыта
				return frame.InfoArr
			}
		}
	}

	return nil
}
////////////////////////////////////////////////////////


// для пульта выдать номера инфо-фукнций, привычных для данных условий
func GetMentalAutomatizmForPult()string{
	mentAtmzmActualFuncs:=getFavoritInfoFunc()
	if mentAtmzmActualFuncs != nil && len(mentAtmzmActualFuncs) >0{
		maStr:=""
		for i:=0;i<len(mentAtmzmActualFuncs);i++ {
			if i>0{maStr+=", "}
			maStr += strconv.Itoa(mentAtmzmActualFuncs[i])
		}
		return maStr
	}
return "Нет привычной цепочки инфо-функций для данных условий."
}
////////////////////////////////////////////////////


____________________________________________

BOT/brain/psychic/episodic_mental_memory_pult.go
____________________________________________
/*  для пульта показ дерева ментальной эпиз.памяти

 */
package psychic

import "strconv"

//////////////////////////////////////////////////
//100 последних кадров, записанных в кадрах эпизодической памяти
//var idEpArr = make(map[int]param)// массивы params для данного ID кадра памяти
func GetMentallastRules()string{

	hStr:="<b>Историческая последовательность кадров:</b><br>"

	// последний индекс исторической памяти EpisodicMentalHistoryArr[]
	lastI:=len(EpisodicMentalHistoryArr)-1
	var node *EpisodicMentalTreeNode
	out:="<hr><br>До 100 последних Правил, записанных в кадрах эпизодической памяти." +
		"<table cellpadding=0 cellspacing=0 border=1 class='main_table'><tr>" +
		"<th class='table_header' title='Номер кадра эпизодической памяти - индекс ячейки исторической памяти'>№ кадра</th>" +
		"<th class='table_header' title='ID кадра эпизодической памяти'>ID</th>" +
		"<th class='table_header' title='ID дерева проблем'>NodePID</th>" +
		"<th class='table_header' title='Тема мышления'>ThemeID</th>" +
		"<th class='table_header' title='Текущая цель'>PurposeID</th>" +
		"<th class='table_header' title='последовательность ID инфо функций, которая применялось после активации NodePID'>InfoArr</th>" +
		"<th class='table_header' title='Эффет от цепочки инфо-функций'>Effect</th>" +
		"<th class='table_header' title='Уверенность'>Уверенность</th></tr>"
	rCount:=0
	style:="style='font-size:19px;font-weight:bold;cursor:pointer'"
	for i := lastI; i >= 0 || rCount>100; i-- {
		curID:=EpisodicMentalHistoryArr[i].ID
		if curID == -1{
			hStr+=frameMentStr(-1)
			continue
		}

		node0,ok:=ReadeEpisodicMentalTreeNodeFromID(curID)
		if !ok {continue }
		node=node0

		infoStr:=""
		for n := 0; n < len(node.InfoArr); n++ {
			if n>0{infoStr+=", "}
			infoStr+="<span onClick='show_mental_func("+strconv.Itoa(node.InfoArr[n])+ ")'>"+strconv.Itoa(node.InfoArr[n])+"</span>"
		}

		pars:=node.PARAMS
		if len(pars)==0{
			continue
		}

		bg:="#ffffff" // фон строки учительского правила - bg=="#FFEBE4"
		// Показываем  node.PARAMS
		effect:=pars[0]

		out+="<tr style='background-color:"+bg+";' title='Каддр памяти'>" +
			"<td >"+strconv.Itoa(i)+"</td>" +
			"<td >"+strconv.Itoa(curID)+"</td>" +
			"<td "+style+" onClick='show_problem_tree("+strconv.Itoa(node.NodePID)+ ")'>"+strconv.Itoa(node.NodePID)+"</td>" +
			"<td "+style+" onClick='show_theme_strings(1,"+strconv.Itoa(node.ThemeID)+")'>"+strconv.Itoa(node.ThemeID)+"</td>" +
			"<td "+style+" onClick='get_purpose(1,"+strconv.Itoa(node.PurposeID)+")'>"+strconv.Itoa(node.PurposeID)+"</td>" +
			"<td "+style+">"+infoStr+"</td>" +
			"<td "+style+">"+strconv.Itoa(effect)+"</td>" +
			"<td "+style+">"+strconv.Itoa(pars[1])+"</td></tr>"
		rCount++
		hStr += frameMentStr(curID)
	}
	if rCount==0{
		out+="<tr><td colspan=7>Не найдены кадры в эпизодической памяти.</td></tr>"
	}
	out+="</table>"

	out= hStr+out

	return out
}
//
func frameMentStr(id int)string{

	if id == -1{
		return "<div class='frameEm' title='пустой кадр - конец темы'>&nbsp;</div>"
	}
	return "<div class='frameEP'>"+strconv.Itoa(id)+"</div>"
}
///////////////////////////////////////////////////////////
____________________________________________

BOT/brain/psychic/episodic_mental_memory_tree.go
____________________________________________
/* дерево ментальных кадров эпизодов памяти
Носитель ментальных правил:
какая цепочка инфо-функций в условиях данной проблемы и темы привела к данному эффекту.
Выполняет роль Ментальных автоматизмов при поиске мент.правила (последователньости инфо-функций) для уверенного запуска в данных условиях.

В файл пишется:
ID|ParentID|NodePID|ThemeID|PurposeID|info1,info2#Effect|Count

Для каждой новой цели должен начинаться свой кадр ментальной эпиз.памяти: clinerFuncSequence()
это делается в func infoFunc8

После отработки wasRunPurposeActionFunc нужно прекратить набор кадров и ждать ответа: func setCurIfoFuncID

Вроде логично, что если не запускалась функция с wasRunPurposeActionFunc=true (id==14,17,26)
- значит не цикл мышления привел к ответу и он не должен учитываться как источник эффекта.
Но даже при запуске автоматизма продолжается осмысление, в том числе и в фоновых циклах и еще есть доминанта.
Есть CurrentInformationEnvironment.needThinkingAboutAutomatizm - думать о создании автоматизма.
Так что пусть пишутся любые цепочки, даже если не было вызова func infoFunc17.
Вообще TODO нужно продолжить поиск более адаптиной реализации ментальных правил...

EpisodicMentalHistoryArr имеет связь с EpisodicHistoryArr через EpisodicMentalHistoryArr.lastEpisodicMemID
 */
package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

////////////////////////////////////////////////////////////

type EpisodicMentalTreeNode struct { // узел дерева Эпизодической памяти
	ID int
	// Узлы дерева:
	/* субъективный узел активной ветки дерева проблем (последний активировавшийся)
	По нему можно найти:
	node,ok:=ReadeProblemTreeNodeFromID()
	if ok{
	NodeAID:=node.autTreeID //конечный узел активной ветки дерева моторных автоматизмов
	NodeSID:=node.situationTreeID //конечный узел активной ветки дерева ситуации
	}*/
	NodePID	int

	/* Темы мышления
	*/
	ThemeID int

	/* Текущая цель
	*/
	PurposeID int
	// последовательность ID инфо функций, которая применялось после активации NodePID
	InfoArr []int // разделитель - запятая!
	// Свойства данного узла - массив PARAMS:
	PARAMS []int
	/* [2] иммет два значения:
	Effect int // эффект от действий: (меньше 0) или 0 или (больше 0)
	Count int // всегда>0 - число усреднений Effect - уверенность в Правиле
	*/

	//Связь узлов дерева:
	Children []EpisodicMentalTreeNode // дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID int     // ID родителя
	ParentNode *EpisodicMentalTreeNode  // адрес родителя
}
//////////////////////////////////////////////////////
var EpisodicMentalTree EpisodicMentalTreeNode
//var EpisodicMentalTreeNodeFromID=make(map[int]*EpisodicMentalTreeNode)
var EpisodicMentalTreeNodeFromID []*EpisodicMentalTreeNode  // сам массив
// запись члена
func WriteEpisodicMentalTreeNodeFromID(index int, value *EpisodicMentalTreeNode) {
	addEpisodicMentalTreeNodeFromID(index)
	EpisodicMentalTreeNodeFromID[index] = value
}
func addEpisodicMentalTreeNodeFromID(index int){
	if index >= len(EpisodicMentalTreeNodeFromID) {
		newSlice := make([]*EpisodicMentalTreeNode, index+1)
		copy(newSlice, EpisodicMentalTreeNodeFromID)
		EpisodicMentalTreeNodeFromID = newSlice
	}
}
// считывание члена
func ReadeEpisodicMentalTreeNodeFromID(index int) (*EpisodicMentalTreeNode,bool){
	if index >= len(EpisodicMentalTreeNodeFromID) || EpisodicMentalTreeNodeFromID[index]==nil {
		return nil,false
	}
	return EpisodicMentalTreeNodeFromID[index],true
}
//////////////////////////////////////////////////////////////////////


/*Связь узлов историческая - ID конечных узлов веток EpisodicMentalTree подряд по порядку возникновения новых.
Записывается в файл "/memory_psy/episodic_history.txt" - сплошная строка EpisodicMentalTreeNode.ID с разделителем "|"
В случе добавления newPARAMS в существующий EpisodicMentalTreeNode пишется его EpisodicMentalTreeNode.ID
так что в EpisodicMentalHistoryArr могут появляться несколько EpisodicMentalTreeNode.ID.

Поиск в EpisodicMentalHistoryArr нужного EpisodicMentalTreeNode.ID
При оптимизации во сне порядок сохраняется, но могут вырезаться отдельные составляющие.
При вырезании из EpisodicMentalHistoryArr пишется в файл episodic_history.txt новая строка
и т.о. EpisodicMentalHistoryArr []int остается с непрерывной цепочкой индексов.

Найти в массиве EpisodicMentalHistoryArr[] индексы всех членов по значению:
iArr:=lib.FindIndexes(EpisodicMentalHistoryArr,ID)

Запись в файле: ID1,LifeTime1|ID2,LifeTime2 ...

EpisodicMentalHistoryArr имеет связь с EpisodicHistoryArr через EpisodicMentalHistoryArr.lastEpisodicMemID
*/
type HistoryMental struct {
	ID int // ID EpisodicMentalTreeNode
	LifeTime int // время появления узла в пульсах от рождения
	// сопутствующий кадр моторной эпиз.памяти
	lastEpisodicMemID int //Запись saveNewEpisodic всегда предшествует с последним кадром lastEpisodicMemID
}
var EpisodicMentalHistoryArr []HistoryMental

///////////////////////////////////////////////////////



// усреднить эффект в PARAMS узла
func averageMentalEffect(node *EpisodicMentalTreeNode,effect int){
	if node.PARAMS == nil{
		return
	}
	// вес нового эффекта
	count:=node.PARAMS[1]
	if count==0{
		lib.TodoPanic("func averageEffect НУЛЕВОЕ ЗНАЧЕНИЕ count")
		return
	}
		w := effect + int(effect / (count+1))
		if w > 10{w=10}
		if w < -10{w=-10}
		node.PARAMS[0]=w
	node.PARAMS[1]=count+1
}
//////////////////////////////////////////



/* ЗАПИСАТЬ В ДЕРЕВО НОВЫЙ ЭПИЗОД

По каждому срабатыванию инфо-фукнций заполняется буфер infoFuncSequence для набора инфоID
Буфер очищается clinerFuncSequence() и в конце func saveNewMentalEpisodic
сбрасывает буфер в func clinerAutomatizmRunning()

Если при осмыслении проходились только уровни до 3-го,
то и нет обработки, нет записи в Эпизод.память ментальных кадров (определяет ощущение субъективного времени).

Только есди в цепочке ID есть 14,17,26(запуска автоматизма) можно судить, что именно этот запуск и привел к эффекту,

Запись saveNewEpisodic всегда предшествует с последним кадром lastEpisodicMemID
 */
func saveNewMentalEpisodic(effect int){
	// последовательность ID инфо-функций, вызываемых после очередной активации
	if infoFuncSequence==nil || len(infoFuncSequence)==0{// так не должно быть при effect!=0
		return
	}
	// не сохранять кадры, в InfoArr которых нет id==17 - запуска автоматизма
	/*НЕТ!  if !lib.ExistsValInArr(infoFuncSequence, 17){
		return
	}*/
	infoFuncSequence=lib.RemoveDuplicates(infoFuncSequence)// удалить повторяющиеся номера

	NodePID:=detectedActiveLastProblemNodID // ID проблемы
	ThemeID:=problemTreeInfo.themeID
	PurposeID:=mentalInfoStruct.mentalPurposeID

	params:= []int{effect,1}
	var condArr = []int{NodePID,ThemeID,PurposeID}

	//проверяются дубликаты
	idOld, nodeOld := checMentalEpisodicBranchFromCondition(NodePID,ThemeID,PurposeID)
	if idOld > 0 {
		EpisodicMentalHistoryArr=append(EpisodicMentalHistoryArr,HistoryMental{idOld,LifeTime,lastEpisodicMemID})
		// усреднить эффект и добавить уверенность
		averageMentalEffect(nodeOld,effect)
		params=nodeOld.PARAMS
	}

	if params !=nil{
		if params[0] > 10 {
			params[0] = 10
		}
		if params[0] < -10 {
			params[0] = -10
		}
	}

	//добавляем новый кадр
	lastNodeID:=addMentalEpisodicFromNodeIDsToBrange(0,0,condArr,infoFuncSequence,params)

	if idOld == 0 {
		EpisodicMentalHistoryArr = append(EpisodicMentalHistoryArr, HistoryMental{lastNodeID, LifeTime,lastEpisodicMemID})
	}

	clinerFuncSequence()// сброс infoFuncSequence
}
////////////////////////////////////////////////////////////





/* Для создания нового узла ветки дерева
Возвращает ID узла и указатель на него - в любом случае, создан ли новый узел или найден такой же уже имеющийся.
*/
var lastIDNodeMentalTree=0 // счетчик ID узлов, хранит ID хранит ID последнего созданного узла
func createEpisodicNodeMentalTree(parent *EpisodicMentalTreeNode,id int,NodePID int,ThemeID int,PurposeID int,InfoArr []int,PARAMS []int) (int,*EpisodicMentalTreeNode) {
	if parent == nil{
		return 0,nil
	}

	if id==0{
		lastIDNodeMentalTree++
		id=lastIDNodeMentalTree
	}else{
		//		newW.ID=id
		if lastIDNodeMentalTree<id{
			lastIDNodeMentalTree=id
		}
	}

	var node EpisodicMentalTreeNode
	node.ID = id
	node.ParentNode=parent
	node.ParentID=parent.ID
	node.NodePID=NodePID
	node.ThemeID=ThemeID
	node.PurposeID=PurposeID
	node.InfoArr=InfoArr
	node.PARAMS=PARAMS

	parent.Children = append(parent.Children, node)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *EpisodicMentalTreeNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
			//		newN.Children=append(newN.Children, node)
		}
	}

	WriteEpisodicMentalTreeNodeFromID(node.ID, &node)

	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в EpisodicMentalTreeNodeFromID:
	// ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
	updatingEpisodicMentalTreeNodeFromID(parent)// здесь потому, что при загрузке из файла нужно на лету получать адреса
	return id,newN
}
//////////////////////////////////////////////////////////


//////////////////////////////////
func checMentalEpisodicBranchFromCondition(NodePID int,ThemeID int,PurposeID int)(int,*EpisodicMentalTreeNode){
	cond:= []int{NodePID,ThemeID,PurposeID}
	//maxLev:=5
	id,level:=findEpisodicMentalBrange(0, cond, &EpisodicMentalTree)
	if id>0{
		// найти уровень lev0 ненулевых параметров
		lev0:=getMentalTrueLevel(cond)
		if lev0 > level{
			return 0,nil
		}
		//if maxLevel==maxLev{// есть такая ветка, просто возвращаем ее значения

		//node,ok:=EpisodicMentalTreeNodeFromID[id]
		node,ok:=ReadeEpisodicMentalTreeNodeFromID(id)
		if ok {
			return id, node
		}else{// такого не должно быть
			lib.TodoPanic("В func FindEpisodicMentalTreeNodeFromCondition должно быть значение карты EpisodicMentalTreeNodeFromID.") //вызвать панику
		}

		//}else{// нужно дорастить ветку
		//	return 0,nil
		//}
	}else{// ID==0 вообще нет совпадений, нужно наращивать с основы
		return 0,nil
	}

	return 0,nil
}
//////////
// найти уровень lev0 ненулевых параметров из массива условий
func getMentalTrueLevel(cond []int)int{
	lev0:=0
	for i := 0; i < len(cond); i++ {
		if cond[i]==0{
			break
		}
		lev0++
	}
	return lev0
}
////////////
// рекурсивно корректируем адреса всех узлов
//ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
func updatingEpisodicMentalTreeNodeFromID(rt *EpisodicMentalTreeNode){
	if rt.ID>0 {
		rt.ParentNode=EpisodicMentalTreeNodeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		_,ok:=ReadeEpisodicMentalTreeNodeFromID(rt.ParentID)
		if ok {
			WriteEpisodicMentalTreeNodeFromID(rt.ID, rt)
		}
	}
	if rt.Children == nil{// конец ветки
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		updatingEpisodicMentalTreeNodeFromID(&rt.Children[i])
	}
}
///////////////////////////////////////////////////////////





////////////////////////////////////////////////
// получить массив PARAMS[2] последнего узла дерева
// Только узел c ActionID имеет PARAMS[][]
func getMentalEpisodicPARAMS(nodeID int)[]int{

	//var actionNode,ok=EpisodicMentalTreeNodeFromID[nodeID]
	actionNode,ok:=ReadeEpisodicMentalTreeNodeFromID(nodeID)
	if !ok{
		return nil
	}

	if len(actionNode.InfoArr)==0{
		return nil
	}

	return actionNode.PARAMS //[]int{}
}
////

/////////////////////////////////////////////////////////////////////////








/* Доращивание ветки, начиная с заданного узла fromID по массиву всех значений для ветки
newPARAMS - добавить к ветке, если она существует новый массив Effect|LifeTime
Возвращает ID конечного узла ветки.
*/
func addMentalEpisodicFromNodeIDsToBrange(fromID int,lastLevel int,condArr []int,InfoArr []int,newPARAMS []int)int{

	var bNode *EpisodicMentalTreeNode
	if fromID >0 {
		//node, ok := EpisodicMentalTreeNodeFromID[fromID]
		node,ok:=ReadeEpisodicMentalTreeNodeFromID(fromID)
		if !ok {
			return 0
		}
		bNode=node
	}else{ // не определен начальный узел, хотя он может и быть
		bNode=&EpisodicMentalTree
	}

	lastNodeID,_:=addMentalEpisodicNodesToBrange(bNode,lastLevel,condArr,InfoArr,newPARAMS)

	return lastNodeID
}
/* рекурсивно создать все недостающие уровни ветки по значениям condArr []int

 */
func addMentalEpisodicNodesToBrange(fromNode *EpisodicMentalTreeNode,level int,condArr []int,InfoArr []int,newPARAMS []int)(int,*EpisodicMentalTreeNode){
	if fromNode==nil {
		return 0,nil
	}
	if level>=len(condArr) {
		return fromNode.ID,fromNode
	}
	//vArr := make([]int, len(condArr))// все vArr[n] имеют нулевые значения
	vArr :=[]int{0,0,0}
	for i := 0; i <= level; i++ {// заполнить vArr до текущего уровня включительно, остальные - оставить нулями
		vArr[i] = condArr[i]
	}
	// с каждым уровнем vArr[n] добавляется новое значение для создания узла ветки следующего уровня.
	// если такой узел есть (checkUnicum==true), то просто вернет его node
	var node *EpisodicMentalTreeNode
	var pars []int
	var info []int

	//проверяются дубликаты потому, что новый узел нужно делать только если такого еще нет!!!
	idOld, nodeOld := checMentalEpisodicBranchFromCondition(vArr[0], vArr[1], vArr[2])

	//node.PARAMS=append(node.PARAMS,pars)
	if idOld > 0 {
		node = nodeOld
		if level==2 {
			if newPARAMS !=nil{
				// усреднить эффект
				averageMentalEffect(node,newPARAMS[0])
			}
		}
	}else { // НОВЫЙ
		if level==2 {
			pars = newPARAMS
			info=InfoArr
		}

		if pars!=nil && pars[0]!=100 {
			if pars[0] > 10 {
				pars[0] = 10
			}
			if pars[0] < -10 {
				pars[0] = -10
			}
		}

		_, node = createEpisodicNodeMentalTree(fromNode, 0, vArr[0], vArr[1], vArr[2], info,pars)
	}

	level++
	if level>=len(condArr) {
		return node.ID,node
	}
	id,node:= addMentalEpisodicNodesToBrange(node,level, condArr, InfoArr,newPARAMS)
	return id,node
}
///////////////////////////////////////



/*  поиск конечного узла ветки (lastBrangeID) дерева (root - ID начального узла == 0) по массиву ID узлов ветки (cond []int)
Поиск начинается с первого узла (по значению cond[0]).
Если первый узел найден, то findSampleBrange вызывается рекурсивно для поиска следующего узла
и так далее, пока не будет найден удел последнего члена cond.
Если для текущего cond[level] узел в редеве не существует, то в дереве доращивается ветка по значениям cond []int.

Возвращает ID последней найденной ветки и ее уровень, если конечный узел не найден (если найден, то len(cond)-1).
Если вернула уровень меньше, чем максимальное число уровней ветки,
то ветка доращивается func addNodesToBrange

Ищет до узла Action включительно, а к этому узлу могут быть прикреплены несколько PARAMS
*/
func findEpisodicMentalBrange(level int, cond []int, root *EpisodicMentalTreeNode) (int,int) {

	// Обработка случая когда мы достигли конца дерева
	if cond == nil || len(cond) <= 0 || level >= len(cond) {
		return root.ID,len(cond)
	}

	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children {
		if isEquivalentMentalCondition(level, &child,cond) {
			// Если узел найден, продолжим рекурсивно искать в нем далее
			id,lev := findEpisodicMentalBrange(level+1, cond, &child)
			if id == 0 {
				/*Eсли условие cond []int содержит значение 0 для какого-то уровня, то на этом уровне поиск заканчивается.
				if cond[lev]==0{// считается нормальный поиск по неполному условию

				}*/
				return child.ID,lev // поиск закончен на child.ID
				//return 0,lev // не найдено совпадение на данном уровне
			}

			return id,lev
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return 0,level
}
////////////////////////////////////////////////////
func isEquivalentMentalCondition(level int, node *EpisodicMentalTreeNode,cond []int)bool{
	// массив значений по уровням - в зависимости от числа уровней в ветке, прописывается вручную
	nArr:= []int{node.NodePID,node.ThemeID,node.PurposeID}

	for i := 0; i < len(cond) && i<=level; i++ {
		if cond[i] != nArr[i] {
			return false
		}
	}
	return true
}
/////////////////////////////////////////////////////////




/* загрузить записанное дерево
 */
func loadEpisodicMentalTree() {
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/episodic_mental_tree.txt")
	initEpisodicMentalTree(strArr)

	str := lib.ReadFileContent(lib.GetMainPathExeFile() + "/memory_psy/episodic_mental_history.txt")

	sArr := strings.Split(str, "|")
	for n := 0; n < len(sArr); n++ {
		if (len(sArr[n]) == 0) {
			break
		}
		il := strings.Split(sArr[n], ",")
		id, _ := strconv.Atoi(il[0])
		lifeTime, _ := strconv.Atoi(il[1])
		lastEpisodicMemID, _ := strconv.Atoi(il[2])
		EpisodicMentalHistoryArr = append(EpisodicMentalHistoryArr, HistoryMental{id,lifeTime,lastEpisodicMemID})
	}

	// ОТЛАДКА, ТЕСТИРОВАНИЕ
	// saveNewEpisodic(67,38,107,1,3,3)
	//	saveNewEpisodic(200,39,100,1,3,3)

	// getEpisodesArrFromConditions(0,curStimulImageID,0)
	// getEpisodesArrFromConditions(0,1,0)
	// getEpisodesArrFromConditions(1,1,0)

	// тестирование getTargetEpisodicStrIdArr()
	testing:=false
	//	testing=true
	if testing {// здесь еще не активированные деревья

	}
	// setMentalInterruptionEpisosde() // обязательно в конце втыкаем пустой кадр!!! Иначе после просыпания начнет дописывать новые кадры к старым цепочкам
	return
}
//В файл пишется: ID|ParentID|NodePID|ThemeID|PurposeID|info1,info2#Effect|Count
func initEpisodicMentalTree(strArr []string){

	cunt:=len(strArr)
	EpisodicMentalTreeNodeFromID = make([]*EpisodicMentalTreeNode, cunt)//задать сразу имеющиеся в файле число при загрузке из файла
	lastIDNodeMentalTree=1
	// определить нулевой узел
	//EpisodicMentalTreeNodeFromID[0]=&EpisodicMentalTree// все по нулям по умолчанию
	WriteEpisodicMentalTreeNodeFromID(0, &EpisodicMentalTree)

	//просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		if len(strArr[n])<2{
			panic("Сбой загрузки дерева: ["+strconv.Itoa(n) + "] " + strArr[n])
			return
		}

		par := strings.Split(strArr[n], "#")
		p:=strings.Split(par[0], "|")
		var parArr []string
		if len(par)>1{
			parArr = strings.Split(par[1], "|")
		}

		id,_:=strconv.Atoi(p[0])
		parentID,_:=strconv.Atoi(p[1])

		NodePID,_:=strconv.Atoi(p[2])
		ThemeID,_:=strconv.Atoi(p[3])
		PurposeID,_:=strconv.Atoi(p[4])

		var InfoArr []int
		iaStr := strings.Split(p[5], ",")
		if len(iaStr)>0 {
			for i := 0; i < len(iaStr); i++ {
				if len(iaStr[i])==0{
					continue
				}
				iID,_:=strconv.Atoi(iaStr[i])
				InfoArr=append(InfoArr,iID)
			}
		}

		// считывание PARAMS
		var params []int
		if PurposeID>0 {
			eff,_:=strconv.Atoi(parArr[0])
			count,_:=strconv.Atoi(parArr[1])
			params = []int{eff,count}
		}

		parentNode,ok:=ReadeEpisodicMentalTreeNodeFromID(parentID)
		if ok {

			// Создание нового узла дерева
			node := EpisodicMentalTreeNode{
				ID:         id,
				NodePID:    NodePID,
				ThemeID:    ThemeID,
				PurposeID:  PurposeID,

				Children:   []EpisodicMentalTreeNode{},
				ParentID:   parentID,
				ParentNode: parentNode,
			}
			node.PARAMS=params
			node.InfoArr=InfoArr

			node.ParentID = parentNode.ID
			node.ParentNode = parentNode
			parentNode.Children = append(parentNode.Children, node)

			// Добавление узла в массив по ID
			WriteEpisodicMentalTreeNodeFromID(id, &node)
			lastIDNodeMentalTree++

			// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в EpisodicMentalTreeNodeFromID:
			// ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
			updatingEpisodicMentalTreeNodeFromID(parentNode)// здесь потому, что при загрузке из файла нужно на лету получать адреса
		}
	}
	return
}
/////////////////////////////////////




//В файл пишется: ID|ParentID|NodePID|ThemeID|PurposeID|info1,info2#Effect|Count
func SaveEpisodicMentalTree(){
	var out=""
	cnt:=len(EpisodicMentalTree.Children)
	for n := 0; n < cnt; n++ { // чтобы записывалось по порядку родителей
		out+=getEpisodicMentalTreeNode(&EpisodicMentalTree.Children[n])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/episodic_mental_tree.txt",out)

	// запись истории эпизодов EpisodicMentalHistoryArr []int
	out=""
	for n := 0; n < len(EpisodicMentalHistoryArr); n++ {
		out+=strconv.Itoa(EpisodicMentalHistoryArr[n].ID)+
			","+ strconv.Itoa(EpisodicMentalHistoryArr[n].LifeTime)+
			","+ strconv.Itoa(EpisodicMentalHistoryArr[n].lastEpisodicMemID) + "|"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/episodic_mental_history.txt",out)

	return
}
func getEpisodicMentalTreeNode(wt *EpisodicMentalTreeNode)string{
	var out=""
	//	if wt.ParentID>0 {
	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|"
	out += strconv.Itoa(wt.NodePID) + "|"
	out += strconv.Itoa(wt.ThemeID) + "|"
	out += strconv.Itoa(wt.PurposeID) + "|"
	for n := 0; n < len(wt.InfoArr); n++ {
		out += strconv.Itoa(wt.InfoArr[n]) + ","
	}

	// записать массивы PARAMS
	parsArr:=getMentalEpisodicPARAMS(wt.ID)
	if parsArr!=nil {
		out +="#"
		out += strconv.Itoa(parsArr[0]) + "|"
		out += strconv.Itoa(parsArr[1])
	}

	out +="\r\n"
	//	}
	if wt.Children==nil{// конец
		return out
	}
	for n := 0; n < len(wt.Children); n++ {
		out+=getEpisodicMentalTreeNode(&wt.Children[n])
	}
	return out
}
/////////////////////////////////////


//Очистить эпизодическую память
func ClianMentalEpisodicMemory(){
	lib.WriteFileContentExactly(lib.GetMainPathExeFile()+"/memory_psy/episodic_mental_tree.txt","")
	lib.WriteFileContentExactly(lib.GetMainPathExeFile()+"/memory_psy/episodic_mental_history.txt","")

	EpisodicMentalHistoryArr=nil
	lastIDNodeMentalTree=1
	WriteEpisodicMentalTreeNodeFromID(0, &EpisodicMentalTree)
}
/////////////////////////////////////



/* вставить кадр прерывания цепочки (темы)   ВСТАВКА ПУСТЫХ КАДРОВ НЕ НУЖНА!!!!просто условия будут разделять.
Означает, что цепочка кадров памяти данной темы закончиена (fornit.ru/67675).
Вставляется по щелчку по плащке ожидания ответа (func StopWaitingWeriodFromOperator())

func setMentalInterruptionEpisosde(){
	// первым не включать
	if len(EpisodicMentalHistoryArr)==0{
		return
	}

	// если только что уже был пустой кадр, то не вставлять лишний
	lastI:=len(EpisodicMentalHistoryArr)-1
	if EpisodicMentalHistoryArr[lastI].ID==-1{
		return
	}
	// пустой кадр с ID=-1
	EpisodicMentalHistoryArr = append(EpisodicMentalHistoryArr, HistoryMental{-1, LifeTime,lastEpisodicMemID})
}*/
///////////////////////////////////////////////////////////
____________________________________________

BOT/brain/psychic/importance.go
____________________________________________
/* Значимость элементов восприятия - как объекта произвольного внимания:
того из всего воспринимаемого, что имеет наибольшую значимость
т.к. именно наибольшая значимость должна осмысливаться.

Кроме того, значимости объектов - это и есть модель понимания данного объекта внимания -
его значимость в разных условиях и то, какие действия могут быть совершены при этом.

Значимость формируется в эпиз.памяти (func saveNewEpisodic) и это всегда значимость образа стимула.
Значимость стимула в PARAMS[2] зависит от count PARAMS[1] - числа подтверждений
func getOpower // сила значимости стимула в зависимости от count

Значимости в коде проекта (func getObjectImportance) обычно имеют значения величин от -10 0 до 10

Объект значимости - всегда имеет тип ActionsImage - конечный интегральный образ стимула, вне зависимости от его компонентов,
т.е. если пришла только фраза, без других компонентов, то при восприятии она имеет тип ActionsImage.
Значимость фразы с тоном или настроением или с действием - уже другая в данных условиях.


При каждом вызове consciousnessElementary определяется текущий объект наибольшой значимости в воспринимаемом -
в функции определения текущей Цели getMentalPurpose().

*/

package psychic

import "BOT/lib"

/////////////////////////////////////////////////

/*
Для определения текущих объектов восприятия и выделения одного из них - самого важного
*/
type extremImportance struct {
	objID     int //  объект значимости
	extremVal int // экстремальная значимость
}

// текущий значимый объект внимания (не сохраняется в файле):
var extremImportanceObject *extremImportance
var extremImportanceObjectOld *extremImportance // против повторения
// текущий значимый объект внимания с отрицательным эффектом extremImportance.extremVal
var problemExtremImportanceObject *extremImportance

/////////////////////////////////////////////

/*
	текущий субъект внимания (объект внимания к собственным мыслям)

используется в func getMentalEffect (Оценка суммарного ментального эффекта в период ожидания),
НУЖНО БЫ ЕГО ГДЕ-ТО выявлять
TODO м.б. в episodic_mental_memory_tree.go
*/
var extremImportanceMentalObject *extremImportance

///////////////////////////////////////////////////////

//////////////////////////////////////////////////////
/*
	найти extremImportanceObject при каждом новом стимуле curActiveActions

перекрывает прежний extremImportanceObject только если найден экстремальный объект
*/
func getExtremImportanceObject() {

	// в режиме сна мышление начинается так же с последнего curActiveActions
	if curActiveActions != nil { // curActiveActions обнуляется
		// объект объективного восприятия curActiveActions типа *ActionsImage - структура действий оператора при активации дерева автоматизмов
		eobj := getExtremObjFromID(curActiveActions.ID)
		if eobj != nil {
			if lib.Abs(eobj.extremVal) > 2 { // достаточно значимый объект
				extremImportanceObject = eobj
				CurrentInformationEnvironment.ExtremImportanceObjectID = eobj.objID
				if lib.Abs(eobj.extremVal) > 3 {
					runNewTheme(16, 2) // есть объект высокой значимости

					if eobj.extremVal < -3 { // проблемный объект с отрицательным эффектом
						// текущий значимый объект внимания с отрицательным эффектом extremImportance.extremVal
						problemExtremImportanceObject = eobj
					}
				}
			}
		}
	}
	return
}

//////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/importance_funcs.go
____________________________________________
/* Функции Значимости объектов восприятия

Значимость всегда определяется в контексте всех предшествующих условий,
т.е. специфична для активностей деревьев автоматизмов и понимания.

При каждом вызове consciousnessElementary определяется текущий объект наибольшой значимости в воспринимаемом -
в функции определения текущей Цели getMentalPurpose().
*/

package psychic

import "BOT/lib"

/////////////////////////////////////////////////

///////////////////////////////////////////////////
/*
найти extremImportance по ActionsImage ID в тек.условиях detectedActiveLastProblemNodID
*/
func getExtremObjFromID(actID int) *extremImportance {

	obj, _ := getObjectsImportanceValue(actID, detectedActiveLastProblemNodID)
	if obj != nil {
		return obj
	}
	return nil
}

/////////////////////////////////////////////////

////////////////////////////////////////////////////
/*
	значимость ID объекта внимания для данного ProblemID (detectedActiveLastProblemNodID)

в условиях ProblemID (текущий: detectedActiveLastUnderstandingNodID)
Возвращает:
1 - объект значимости *extremImportance
2 - значимость
*/
var extremImportanceFind *extremImportance // найденная экстремальная значимость
func getObjectsImportanceValue(ObjectID int, ProblemID int) (*extremImportance, int) {
	if ObjectID == 0 {
		return nil, 0
	}
	extremImportanceFind = nil
	cond := []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID, ProblemID}
	findEpisodicBrangeImportance(0, cond, &EpisodicTree, ObjectID)
	if extremImportanceFind != nil {
		return extremImportanceFind, extremImportanceFind.extremVal
	}
	return nil, 0
}

// поиск конечного узла ветки (lastBrangeID) дерева (root - ID начального узла == 0) по массиву ID узлов ветки (cond []int)
func findEpisodicBrangeImportance(level int, cond []int, root *EpisodicTreeNode, ObjectID int) {

	if cond == nil || len(cond) <= 0 || level >= len(cond) { // Обработка случая когда мы достигли конца дерева
		//	return   !кроме условий тут еще 1 или 2 уровня прохода в поиске ObjectID
	}
	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children { // пока есть дочки
		if isEquivalentCondition(level, &child, cond) {
			if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
				/* первая строка - для прямого правила,
				вторая - для учительского:
				*/
				if (child.Trigger == ObjectID && child.PARAMS[0] < 100) ||
					(child.Action == ObjectID && child.PARAMS[0] == 100) {
					//extremImportanceFind.objID = ObjectID  все равно приходится каждый раз создавать объект...
					//extremImportanceFind.extremVal = child.PARAMS[2]
					extremImportanceFind = &extremImportance{ObjectID, child.PARAMS[2]} //неизбежная утечка памяти...
					return                                                              // нашли
				}
			}
			// Если узел найден, продолжим рекурсивно искать в нем далее
			findEpisodicBrangeImportance(level+1, cond, &child, ObjectID)
			return
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return
}

/////////////////////////////////////////////////////

/* для текущих условий выбрать самое значимое правило bestRule из всех найденных foundRulesArr
 */
var bestRule Rule        // результат поиска
var foundRulesArr []Rule // сбор всех найденных
func getBestRuleFromImpotrents() {
	foundRulesArr = nil
	bestRule.Count = 0 // маркер, что не найден
	cond := []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID, detectedActiveLastUnderstandingNodID}
	findBestRuleFromImpotrents(0, cond, &EpisodicTree)
	if len(foundRulesArr) > 0 { // найти наилучшую значимость
		choseBestRuleFromImpotrents()
	}

}
func findBestRuleFromImpotrents(level int, cond []int, root *EpisodicTreeNode) {
	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children { // пока есть дочки
		if isEquivalentCondition(level, &child, cond) {
			if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
				if lib.Abs(child.PARAMS[2]) > 0 {
					foundRulesArr = append(foundRulesArr, Rule{child.Trigger, child.Action, child.PARAMS[0], child.PARAMS[1], child.PARAMS[2]})
				}
			}
			// Если узел найден, продолжим рекурсивно искать в нем далее
			findBestRuleFromImpotrents(level+1, cond, &child)
			return
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return
}
func choseBestRuleFromImpotrents() {
	curI := 0
	for _, r := range foundRulesArr {
		if r.Importence > 0 {
			if r.Importence*r.Count > curI {
				curI = r.Importence * r.Count
				bestRule = r
			}
		}
	}
}

/////////////////////////////////////////////////////////

/*
знаком ли образ actID для данных условий? Новизна.
*/
func isUnknownActionsImage(actID int) bool {
	obj, _ := getObjectsImportanceValue(actID, detectedActiveLastProblemNodID)
	if obj != nil {
		return true
	}
	return false
}

//////////////////////////////////////////////////////////

/*
Найти действие в учительских правилах (стимул EpisodicTreeNode.Action),
которое имеет наивысшую значимость в данных условиях
чтобы использовать в качестве ответного действия (как элемент фантазирования).
*/
var chooseBestActionID = 0
var findPositiveActionArr []extremImportance

func findBestPositiveAction() int {
	chooseBestActionID = 0
	// прямо проходим условия
	cond := []int{CurrentCommonBadNormalWell, CurrentEmotionReception.ID, detectedActiveLastProblemNodID}
	findEpisodicBestPositiveAction(0, cond, &EpisodicTree)
	chooseBestAction()
	if chooseBestActionID > 0 {
		return chooseBestActionID
	}
	return 0
}
func findEpisodicBestPositiveAction(level int, cond []int, root *EpisodicTreeNode) {

	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children { // пока есть дочки
		if isEquivalentCondition(level, &child, cond) {
			if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
				if (child.PARAMS[0] == 100) && (child.PARAMS[2] > 2) {
					findPositiveActionArr = append(findPositiveActionArr, extremImportance{child.Action, child.PARAMS[2] * child.PARAMS[1]})
				}
			}
			// Если узел найден, продолжим рекурсивно искать в нем далее
			findEpisodicBestPositiveAction(level+1, cond, &child)
			return
		}
		//  пусть перебирает дочки! return 0,level // не найдено совпадение на данном уровне
	}

	// не найден узел на данном уровне
	return
}
func chooseBestAction() {
	maxV := 0
	for _, ex := range findPositiveActionArr {
		if ex.extremVal > maxV {
			maxV = ex.extremVal
			chooseBestActionID = ex.objID
		}
	}
}

/////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/mental_automatism_tree_pult_show.go
____________________________________________
/*  Выдать на Пульт дерево ментальных автоматизмов
id|Usefulness|ActionsImageID|motAutmtzmID
*/


package psychic

import (
	"strconv"
)

////////////////////////////////////////////

/*запрет показа карты на пульте (func GetAutomatizmTreeForPult()) при обновлении
против паники типа "одновременная запись и считывание карты"
Использовать для всех операций записи узлов дерева
*/


// образ дерева автоматизмов для вывода
var automatizmMentalTreeModel=""
/////////////////////////////////////////////////
func GetMentalAutomatizmTreeForPult(limit int)(string){
	// против паники типа "одновременная запись и считывание карты"
	if notAllowScanInTreeThisTime{
		return "!Временно запрещена работа func GetAutomatizmTreeForPult() т.к. идет параллельная обработка."
	}
	if len(UnderstandingTree.Children)==0 { // еще нет никаких веток
		return "Еще нет Дерева ситуации"
	}

//посмотреть число имеющихся узлов дерева
	//strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/automatizm_tree.txt")
	automatizmMentalTreeModel="" // иначе дублирует блоки дерева при каждом выводе страницы
	scanMentalAutomatizmNodes(-1, &UnderstandingTree)

	if len(automatizmMentalTreeModel)<10{
		return "Еще нет информационных веток дерева"
	}

	return automatizmMentalTreeModel

}
//////////////////////
// ID|ParentNode|Mood|EmotionID|SituationID|PurposeID
func scanMentalAutomatizmNodes(level int,node *UnderstandingNode) {

	if node.ID > 0 {
		automatizmMentalTreeModel +="<span style='color:#666666;'>"+strconv.Itoa(node.ID)+":</span> "

		automatizmMentalTreeModel += setOutShift(level)

		switch level {
		case 0:// Mood
			automatizmMentalTreeModel +=getMoodStr(node)
		case 1: // EmotionID
			automatizmMentalTreeModel +=getEmotionStr(node)
		case 2: // SituationID
			automatizmMentalTreeModel +=getSituationStr(node)
		//case 3: // PurposeID
		//	automatizmMentalTreeModel +=getPurposeStr(node)
		}
		automatizmMentalTreeModel +="<br>\n"
	}
		level++
		for n := 0; n < len(node.Children); n++ {
			scanMentalAutomatizmNodes(level, &node.Children[n])
		}
}

// отступ
func setOutShift(level int)(string){
	var sh=""
	for n := 0; n < level; n++ {
		sh+="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	}
	return sh
}

func GetMentalSituationsForNodeInfo(sID int)(string){
//	st:=SituationImageFromIdArr[sID]
	st,ok:=ReadeSituationImageFromIdArr(sID)
	if !ok{
		return "Нет образа ситуации с ID="+strconv.Itoa(sID)
	}

	switch st.SituationType{
	case 1: return "Было ответное действие"
	case 2: return "Был запуск автоматизма ветки"
	case 3: return "Ничего не делали, но нужно осмысление"
	case 4: return "Все спокойно, можно экспериментировать"
	case 5: return "Оператор не прореагировал на действия в течение периода ожидания"

	case 11: return "Оператор выбрал настроение Хорошее"
	case 12: return "Оператор выбрал настроение Плохое"
	case 13: return "Оператор выбрал настроение Игровое"
	case 14: return "Оператор выбрал настроение Учитель"
	case 15: return "Оператор выбрал настроение Агрессивное"
	case 16: return "Оператор выбрал настроение Защитное"
	case 17: return "Оператор выбрал настроение Протест"

	//case 20: return "Оператор нажал кнопку "
	case 21: return "Оператор нажал кнопку Непонятно"
	case 22: return "Оператор нажал кнопку Понятно"
	case 23: return "Оператор нажал кнопку Наказать"
	case 24: return "Оператор нажал кнопку Поощрить"
	case 25: return "Оператор нажал кнопку Накормить"
	case 26: return "Оператор нажал кнопку Успокоить"
	case 27: return "Оператор нажал кнопку Предложить поиграть"
	case 28: return "Оператор нажал кнопку Предложить поучить"
	case 29: return "Оператор нажал кнопку Игнорировать"
	case 30: return "Оператор нажал кнопку Сделать больно"
	case 31: return "Оператор нажал кнопку Сделать приятно"
	case 32: return "Оператор нажал кнопку Заплакать"
	case 33: return "Оператор нажал кнопку Засмеяться"
	case 34: return "Оператор нажал кнопку Обрадоваться"
	case 35: return "Оператор нажал кнопку Испугаться"
	case 36: return "Оператор нажал кнопку Простить"
	case 37: return "Оператор нажал кнопку Вылечить"
	}
	return ""
}

func GetMentalPurposeForNodeInfo(pID int)(string){
	out:="Цель - "
	outMode:=""
	outEmo:=""
	outSit:=""
//	pp:=PurposeImageFromID[pID]
	pp,ok:=ReadePurposeImageFromID(pID)
	if !ok {
		return "Нет образа цели для pID="+strconv.Itoa(pID)+" - в func GetMentalPurposeForNodeInfo"
	}

	switch {
	case pp.moodeID<0:
		outMode="плохого настроения<br>"
	case pp.moodeID==0:
		outMode="нормального настроения<br>"
	case pp.moodeID>0:
		outMode="хорошего настроения<br>"
	}
	if pp.emotonID>0{
		em:=EmotionFromIdArr[pp.emotonID]
		outEmo="ID эмоции ="+strconv.Itoa(pp.emotonID)+": "+getEmotonsComponentStr(em, false)+"<br>"
	}
	if pp.situationID>0{
		outSit="ID ситуации ="+strconv.Itoa(pp.situationID)+": "+getSituationString(pp.situationID)+"<br>"
	}

	if pp.target==1{
		out+="повторение:<br>"
	}else{
		out+="улучшение:<br>"
	}

	out+=outMode
	if outEmo !="" {out+=outEmo}
	if outSit !="" {out+=outSit}

	return out
}

func GetMentalThemeForNodeInfo(tID int)(string) {
	out := ""

//	tImg:=ThemeImageFromID[tID]
	tImg,ok:=ReadeThemeImageFromID(tID)
	if ok {
		out += "Тип темы: <b>" + GetThemeImageName(tImg.Type) + "</b><br>"
		out += "Вес значимости темы: <b>" + strconv.Itoa(tImg.Weight) + "</b><br>"
		diff := LifeTime - tImg.PulsCount
		out += "Время жизни темы: <b>" + strconv.Itoa(diff) + " секунд</b><br>"
		return out
	}
	return ""
}

// Mood
func getMoodStr(node *UnderstandingNode)(string){
	return getMoodString(node.Mood)
}
func getMoodString(id int)string{
	moodS := ""
	switch id {
	case -1:
		moodS = "Плохое"
	case 0:
		moodS = "Нормальное"
	case 1:
		moodS = "Хорошее"
	}
	out := "Настроение: <b> " + moodS + "</b>"
	return out
}

// EmotionID
func getEmotionStr(node *UnderstandingNode)(string){
	return getEmotionString(node.EmotionID)
}
func getEmotionString(id int)string{
	em := EmotionFromIdArr[id]
	out := "Эмоция (" + strconv.Itoa(em.ID) + "):<b> " + getEmotonsComponentStr(em, false) + "</b>"
	return out
}

// SituationID
func getSituationStr(node *UnderstandingNode)(string){
	return getSituationString(node.SituationID)
}
func getSituationString(id int)string{

	//pp := SituationImageFromIdArr[id]
	out:=""
	_,ok:=ReadeSituationImageFromIdArr(id)
	if !ok {
		out += "<span style='color:red'>Нет образа Ситуации с ID=" + strconv.Itoa(id)+"</span>"
	} else {
		out += "Ситуация: <b> <span style='cursor:pointer;color:blue' onClick='get_situation(" + strconv.Itoa(id) + ")'>" + strconv.Itoa(id) + "</span>" + "</b>"
	}
	return out
}
func getSituationDetaileString(id int)string{
//	pp := SituationImageFromIdArr[id]
	out:=""
	_,ok:=ReadeSituationImageFromIdArr(id)
	if !ok {
		out += "<span style='color:red'>Нет образа Ситуации с ID=" + strconv.Itoa(id)+"</span>"
	} else {
		out += "Ситуация: <b> "+ GetMentalSituationsForNodeInfo(id) + "</b>"
	}
	return out
}


____________________________________________

BOT/brain/psychic/mental_automatizm_INFO_functions.go
____________________________________________
/* Информационные функции, вызываемые как действия ментального автоматизма по их ID функции.
Так же они могут вызываться непосредсвтенно из процессов функции осознания.

Инфо-функции - разные методы получения инфы, систематизации, поиска и т.п.
с целью найти верное действие для моторного автоматизма, а если нет,
то создания нового ментального автоматизма для продолжения итеации поиска.

У инфо-функций не должно быть аргумента, иначе невозможно будет их вызывать из runMentalFunctionID(id int)
Поэтому в инфо-функции могут вызываться вспомогательные функции с аргументами, полученными в инфофункци
которые вызываются только если есть нужная инфа, например, сохраненная в mentalInfoStruct

Результат работы инфо-функции записывается в mentalInfoStruct

________________________________ПРИМЕР
В цикле мышления нужно запустить имеющийся мот.автоматизм с aID.
Где-то можно просто: infoFunc17()//запустить моторный автоматизм mentalInfoStruct.motorAtmzmID
или
//С использованием образа запуска моторного автоматизма:
maID,ma:=CreateNewlastMentalActionsImagesID(0,6,aID,true)
и тогда infoFunc2()//Подобрать ActionsImage для последующего звена цепочки
видя, что до него есть такой образ, может решить запустить его:
infoFunc7()//запустить моторный автоматизм mentalInfoStruct.motorAtmzmID

Для новых мот.автоматизмов:
infoFunc7()//создать новый моторный автоматизм по действию ActionsImageID
затем
infoFunc7()//запустить моторный автоматизм mentalInfoStruct.motorAtmzmID

TODO Предствоит добавить "размышление" о значимых объектах -
проход по Правилам чтобы посмотреть, чем это может кончиться
и при этом записать новые превила (переработка информации в свободное время).
То, что должно происходить и в сновидениях, но не ограничивая уровни осознания.
Это уже создаст немало шагов мышления в цикле.

Возвращаемое значение нужно для определения cycle.idle

ПРО ОТЗЕРКАЛИВАНИЕ или Как формируются новые автоматизмы с неизвестными ранее действиями?
Отзеркаливание - единственный способ получения автоматизмов с новыми фразами и действиями.
Могут быть следующие случаи.
1. Каждый стимул с Пульта, пришедший в ответ на совершенное действие, создает учительское Правило в эпиз.памяти. Отсюда сразу возникает то, чем нужно отвечать в аналогичных условиях.
2. Стимул с Пульта пришел не связанный с действиями Beast (более 60 пульсов от последнего действия).
В таком случае может быть тут же опробовано "попугайство" (func infoFunc13 В отличии от молчаливогоо отзеркаливания в infoFunc11() для 4-й стадии), что покажет Оператору, что Beast хочет оценить возможный эффект.
3. На 4-й стадии нужно отзеркаливать только то, что в действиях оператора достигает поставленной цели (func infoFunc11 ).
Отзеркаленную фразу можно поначалу испоьзовать как есть, без изменений, но если они в новых условиях дает нежелательный эффект, то она может быть "исправлена" так, чтобы содержать только слова, которые раньше приводили к позитивному эффекту в последовательности кадров эпиз.памяти.
*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/brain/transfer"
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
)

// /////////////////////////////////////////////////////////////////////////
/*
	Дополнительное инфо-окружение. Оперативная память цикла осмысления.

Общая для всех информационных функций структура (типа информацонного окружения)
для сохранения найденной информации.
*/
type mentalInfo struct {
	ActionsImageID int //ID ActionsImage моторного действия (стимула или ответа)
	/* AmtzmNextStringID - наращивание цепочки последовательности действий
	   	для произвольного выполнения или прикрепления к автоматизму.
	   Можно произвольно набирать и корректировать AmtzmNextStringID.
	   Можно произвольно выполнять отдельные ActionsImageID и смотреть результат
	   	чтобы составлять цепочку AmtzmNextStringID.

	   !AmtzmNextStringID - ID без добавленного prefixActionIdValue!
	*/
	AmtzmNextStringID int // последовательность образов действий AmtzmNextString.ID

	toAutmtzmActionsImageID int  //в infoFunc2() создать автоматизм infoFunc7() по ID ActionsImage моторного действия (стимула или ответа)
	noStaffAtmzmID          bool // true - нет шаштного моторного автоматизма
	motorAtmzmID            int  // ID моторного автоматизма
	motorAtmzmBlockedID     int  // ID заблокированного как опасного моторного автоматизма
	runMotorAtmzmID         int  // ID моторного автоматизма который нужно запустить в infoFunc2()
	//var runningMotAutmtzmID=0 - запущенный в infoFunc17() моторный автоматизм
	//mentalAtmzmID int // ID ментального автоматизма
	ThemeImageType   int  // ТИП актуальной темы размышления
	mentalPurposeID  int  // ID ментальной цели, альтернативной текущей  PurposeImage
	notOldAutomatizm bool // true - НЕ позволить запустить рвущийся на выполнение старый автоматизм
	runInfoFuncID    int  // запуск инфо-функции
	epizodFrameIndex int  // ID успешной инфо-функции со все большим отклонением от условий - по ментальным правилам
	volutionReload   int  // для функ 10: 0 не найдены параметры перезапуска дерева, 1 - найдены
	// для произвольной переактивации дерева ситуации infoFunc14():
	moodeID     int
	emotonID    int
	SituationID int
	fromNextID  int // передача инфы о номере шага итерации
	ExtremObjID int // extremImportanceObject.extremObjID - при возврате к прерванному объекту мышления

	DominantaID           int // ID доминанты для коотрой переданы науденные действия
	DominantSuccessAImgID int // найдено успешное действие
	DominantSuccessValue  int // оцененная успешность: 1 - доминанта решена, 2 - не точно, 3 - еще менее точно
	prognoseEffect        int // <0 - плохой прогноз цепочки правил, >0 - хороший прогно(з начиная с действия рвущегося автоматизма)

	noOperatorStimul bool // не было стимула от оператора > 10 сек при значительном изменении гомео-параметров - режим провокации: бот пытается вызвать реакцию оператора

}

var mentalInfoStruct mentalInfo

func clinerMentalInfo() {
	mentalInfoStruct.ActionsImageID = 0
	mentalInfoStruct.AmtzmNextStringID = 0
	mentalInfoStruct.toAutmtzmActionsImageID = 0
	mentalInfoStruct.runMotorAtmzmID = 0
	mentalInfoStruct.noStaffAtmzmID = false
	mentalInfoStruct.motorAtmzmBlockedID = 0
	mentalInfoStruct.motorAtmzmID = 0
	//mentalInfoStruct.mentalAtmzmID=0
	// никогда не очищать Цель! только перекрывать новой	mentalInfoStruct.mentalPurposeID=0 Иначе просто зацикливается
	mentalInfoStruct.notOldAutomatizm = false
	mentalInfoStruct.runInfoFuncID = 0 // запуск инфо-функции
	mentalInfoStruct.epizodFrameIndex = 0
	// для произвольной переактивации дерева ситуации infoFunc14():
	mentalInfoStruct.moodeID = 0
	mentalInfoStruct.emotonID = 0
	mentalInfoStruct.SituationID = 0
	mentalInfoStruct.volutionReload = 0
	mentalInfoStruct.ExtremObjID = 0

	mentalInfoStruct.DominantaID = 0
	mentalInfoStruct.DominantSuccessAImgID = 0
	mentalInfoStruct.DominantSuccessValue = 0
	mentalInfoStruct.prognoseEffect = 0

	mentalInfoStruct.noOperatorStimul = false

	// ВСТАВЛЯТЬ ДРУГИЕ ЧЛЕНЫ ПО МЕРЕ ПОЯВЛЕНИЯ !!!!

}

/*
	произвольно активированные параметры, определяются при замуске ментального автоматизма.

Держатся на время, пока не изменятся генетически определенные соотвествующие параметры или

	если активация была в данном пульсе
*/
var mentalMoodVolitionID = 0        // произвольно активированное настроение
var mentalMoodVolitionPulsCount = 0 // произвольно активированное настроение

var mentalEmotionVolitionID = 0        // произвольно активированная эмоция
var mentalEmotionVolitionPulsCount = 0 // произвольно активированная эмоция

var mentalSituationVolitionID = 0        // произвольно активированная ситуация
var mentalSituationVolitionPulsCount = 0 // произвольно активированная ситуация

//var mentalPurposeImageID=0// призвольно активированная цель
//var mentalPurposeImagePulsCount=0// призвольно активированная цель

var runningMotAutmtzmID = 0 // запущенный в infoFunc17() моторный автоматизм - для MentalAutomatizm.motAutmtzmID

func GetgetInfoFuncInfoStr(fID int) string {
	return getMentalFunctionString(fID)
}

func getMentalFunctionString(id int) string {
	switch id {
	case 1:
		return "Подготовить продолжение цикла осмысления"
	case 2:
		return "Выбрать о чем и как подумать, с учетом предыдущих шагов цикла"
	case 3:
		return "Найти подходящую инфо-функцию по опыту ментальных Правил"
	case 4:
		return "Находит и запускает мот.автоматизм в плохой ситуации"
	case 5:
		return "Идет цикл мышления об экстремально важном объекте extremImportanceObject"
	case 6:
		return "Подвергнуть сомнению автоматизм, если нет опасности (не нужно реагировать аффектно) и ситуация важна"
	case 7:
		return "Создать новый моторный автоматизм по действию ActionsImageID"
	case 8:
		return "Ментальное определение ближайшей Цели в данной ситуации"
	case 9:
		return "Найти способ улучшения значимости объекта внимания extremImportanceObject"
	case 10:
		return "Вспомнить самое значимое, что было для начала размышения (кроме возврата на прерванное) с произвольной переактивацией дерева"
	case 11:
		return "Ментальное отзеркаливание действия оператора"
	case 12:
		return "Cинтез новой фразы из компонентов, имеющих плюсы в Правилах"
	/*case 122:
	return "Выбрать ID действия имеющего плюсы в Правилах"
	*/
	case 13:
		return "Тупое повторение Стимула оператора. Попугайство."
	case 14:
		return "Ментально переактиваровать дерево понимания с заданными узлами"
	case 15:
		return "Для условия дерева автоматизмов (NodeAID) в одиночных Правилах выбираем наилучшее"
	case 16:
		return "Случайно выдать любую известную фразу и затем infoFunc7() + infoFunc17()"
	case 17:
		return "Запустить моторный автоматизм mentalInfoStruct.motorAtmzmID"
	case 18:
		return "Ощущение наиболее значимого из параметров CurrentInformationEnvironment"
	case 19:
		return "Было эвристическое озарение и найдено mentalInfoStruct.ActionsImageID для пробы действия"
	case 20:
		return "Запустить процесс осмысления НАЗАД эпиз.памяти."
	case 202:
		return "Запустить процесс осмысления ВПЕРЕД эпиз.памяти."
	case 21:
		return "Срочно найти какое-то подходящее действие и запустить его"
	case 22:
		return "Добавление нового действия mentalInfoStruct.ActionsImageID в цепочку действий автоматизма Automatizm.NextID"
	case 23:
		return "Создание новой цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID\nс добавлением mentalInfoStruct.motorAtmzmID"
	case 24:
		return "Добавление нового звена mentalInfoStruct.ActionsImageID в цепочку произвольных действий mentalInfoStruct.AmtzmNextStringID"
	case 25:
		return "Посмотреть условия чтобы проявить инициативу"
	case 26:
		return "Запуск на выполнение на пульте имеющейся цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID"
	case 27:
		return "Создание автоматизма на основе имеющейся цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID"
	case 28:
		return "фантазия - как функция интенсивного подбора ассоциаций для решения доминанты"
	case 29:
		return "Более внимательно рассмотреть ситуацию с Правилами: ментальными и связанными с ними моторными"
	case 30:
		return "Анализировать длинную или недоопредеелнную фразу и найти обобщающие данные"
	case 300:
		return "найти в неопределенной фразе что-то ранее понятное и выполнить действия с понятным стимулом"
	case 31:
		return "Провокационные действия, когда нет стимула от оператора, но нужно что-то делать"

		//	case NN: retrun "Решение по опыту закрытых доминант"

		/* !!! кроме этого есть специализированные инфо-функции для поддержки пассивного режима мышления,
		определенные в dreaming.go:
		func GotoDreaming
		func gotoPassiveMaind
		func epizodicGPTprognoze

		*/
	}
	return "Нет функции с ID = " + strconv.Itoa(id)
}

//////////////////////////////////////////////////////////

/*
	Функция вызова пронумерованной функции

Нужна для вызова случайной функции и т.п.
*/
func runMentalFunctionID(c *cycleInfo, id int) bool {
	switch id {
	case 1:
		infoFunc1(c)
		return true //Подготовить продолжение цикла осмысления
	case 2:
		infoFunc2(c)
		return true //выбрать о чем подумать, с учетом предыдущих шагов цикла
	case 3:
		infoFunc3(c)
		return true //найти подходящее мент.действие по опыту ментальных Правил
	case 4:
		infoFunc4(c)
		return true //Находит и запускает мот.автоматизм в плохой ситуации
	case 5:
		infoFunc5(c)
		return true //Идет цикл мышления об экстремально важном объекте extremImportanceObject
	case 6:
		infoFunc6(c)
		return true //ПОДВЕРГНУТЬ СОМНЕНИЮ моторный автоматизм, если нет опасности (не нужно реагировать аффектно) и ситуация важна
	case 7:
		infoFunc7(c)
		return true //создать новый моторный автоматизм по действию ActionsImageID
	case 8:
		infoFunc8(c)
		return true //Ментальное определение ближайшей Цели в данной ситуации
	case 9:
		infoFunc9(c)
		return true //найти способ улучшения значимости объекта внимания extremImportanceObject
	case 10:
		infoFunc10(c)
		return true //вспомнить самое значимое, что было для начала размышения (кроме возврата на прерванное) по InformationEnvironmentObjects []*InformationEnvironment
	case 11:
		infoFunc11(c)
		return true //Ментальное отзеркаливание действия оператора
	case 12:
		infoFunc12(c)
		return true //Cинтез новой фразы из компонентов, имеющих плюсы в Правилах
	/*case 122:
	infoFunc122(c)
	return true //выбрать ID действия имеющего плюсы в Правилах
	*/
	case 13:
		infoFunc13(c)
		return true //Тупое повторение Стимула оператора. Попугайство.
	case 14:
		infoFunc14(c)
		return true //ментально переактиваровать дерево понимания с заданными узлами
	case 15:
		infoFunc15(c)
		return true //Для условия дерева автоматизмов (NodeAID) в одиночных Правилах выбираем наилучшее
	case 16:
		infoFunc16(c)
		return true //Случайно выдать любую известную фразу или действие и затем infoFunc7() + infoFunc17()
	case 17:
		infoFunc17(c)
		return true //запустить моторный автоматизм mentalInfoStruct.motorAtmzmID
	case 18:
		infoFunc18(c)
		return true //Ощущение наиболее значимого из параметров CurrentInformationEnvironment
	case 19:
		infoFunc19(c)
		return true // было эвристическое озарение и найдено mentalInfoStruct.ActionsImageID для пробы действия
	case 20:
		infoFunc20(c)
		return true // выбрать кадр эпиз.памяти dreamingEpisodeHistoryID и проходить назад
	case 202:
		infoFunc202(c)
		return true // выбрать кадр эпиз.памяти dreamingEpisodeHistoryID и проходить вперед
	case 21:
		infoFunc21(c)
		return true // Срочно найти какое-то подходящее действие и запустить его
	case 22:
		infoFunc22(c)
		return true // Добавление нового действия в цепочку действий автоматизма Automatizm.NextID
	case 23:
		infoFunc23(c)
		return true //Создание новой цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID с добавлением mentalInfoStruct.motorAtmzmID
	case 24:
		infoFunc24(c)
		return true //добавление нового звена mentalInfoStruct.motorAtmzmID в цепочку произвольных действий mentalInfoStruct.AmtzmNextStringID
	case 25:
		infoFunc25(c)
		return true //Посмотреть условия чтобы проявить инициативу
	case 26:
		infoFunc26(c)
		return true //Запуск на выполнение на пульте имеющейся цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID
	case 27:
		infoFunc27(c)
		return true //Создание автоматизма на основе имеющейся цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID
	case 28:
		infoFunc28(c)
		return true //фантазия - как функция интенсивного подбора ассоциаций для решения доминанты
	case 29:
		infoFunc29(c)
		return true //Более внимательно рассмотреть ситуацию с Правилами: ментальными и связанными с ними моторными
	case 30:
		infoFunc30(c)
		return true //Анализировать длинную или недоопредеелнную фразу и найти обобщающие данные
	case 300:
		infoFunc300(c)
		return true //найти в неопределенной фразе что-то ранее понятное и выполнить действия с понятным стимулом
	case 31:
		infoFunc31(c)
		return true //"Провокационные действия, когда нет стимула от оператора, но нужно что-то делать"
	default:
		return false
	}

	return false
}

//////////////////////////////////////////////////

//////////////////////////////////////////////////////////
/* далее идут ПРОНУМЕРОВАННЫЕ ИНФОРМАЦИОННЫЕ ФУНКЦИИ,
для которых в mental_automatizm_INFO_structs.go определяются ИНФОРМАЦИОННЫЕ ГЛОБАЛЬНЫЕ СТРУКТУРЫ - для
передачи в них полученной информации.
Так же для передачи информации в инфо-функции (если это нужно, например, что найти) применяюися входне структуры.
*/
//////////////////////////////////////////////////////////

/*
	НЕ НУЖНО БОЛЬШЕ

1. Подготовить ПРОДОЛЖЕНИЕ (а не новый) цикла осмсысления.
Вызывается только из consciousnessElementary

Новый цикл делается в fromNextID := createNewCycleIteration()
*/
func infoFunc1(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	setCurIfoFuncID(c, 1)
	/* может вызываться подряд при пустом цикле
	if c.lastFuncID == 1{// не вызывать, если только что было
		return
	}*/
	c.lastFuncID = 1
	//////////////////////

	//	runningMotAutmtzmID=0
	//	dreamingEpisodeHistoryID=0// прервать размышление, сновидение
	//	curActiveActions=nil // новый шаг мышления не начинать с объекта восприятия curActiveActions

	// продолжить циркуляцию через 1 сек
	//if true {// true - бесконечные параллельные циклы вызывают паники по разным случаям, нужно вылавливать...
	//	sleepCycle(c.ID) // задержать перезапуск на 1 сек
	//}

	//	reloadConsciousness(c,fromNextID)
	return true
}

/////////////////////////////////////////////////////////
/*
func sleepCycle(runID int) {
	cChannelbig := make(chan int,1)

	go func() {
		cChannelbig <- runID

		timer := time.NewTimer(1000 * time.Millisecond)
		select {
		case <-timer.C:
//			consciousnessElementary(2, runID)
		}

		close(cChannelbig) // закрываем канал
	}()
}*/
////////////////////////////////////////////////////////

////////////////////////////////////////////////////
/* №2 - выбрать о чем подумать, с учетом предыдущих шагов цикла.

Уже не были выбраны ментальные Правила, НУЖНО ПРИДУМАТЬ ЧТО-ТО НОВОЕ,
в том числе новый вариант продолжения мент-х Правил при предшествующей коррекции цикла мент.автоматизма.

Здесь выбор той или иной функции делается в контексте имеющейся инфо-среды,
а если этого не удается, то выбирается случайно одна из инфо-функций.

Если удалось найти решение в виде подходящео действия, то тут же оно и запускается.

Не обязательно - ответ на Стимул, а может быть собственная ИНИЦИАТИВА при дреме
(начинается со случайных действий infoFindRundomMentalFunction()).

Возвращаемое значение нужно для определения cycle.idle
*/
func infoFunc2(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 2 { // не вызывать, если только что было
		return false
	}
	if motorActionEffect > 0 && !c.dreaming { // не нужно искать решение, раз уже был нормальный ответ
		return false
	}
	setCurIfoFuncID(c, 2)
	c.log += "Запущена Инфо-функция № 2: <i>\"" + getMentalFunctionString(2) + "\"</i><br>"
	return mentalSimpleReflexSolution(c)
}
func mentalSimpleReflexSolution(c *cycleInfo) bool {

	if c.isWaitingPeriod { // не совершать действий, а ждать ответа
		//TODO думать о сделанном только что, если нет полной уверенности, и успеть еще раз ответить до ответа с пульта?
		// хотя это должен быть личный опыт... лучше не нагромождать.
		return false
	}
	// нельзя прерывать главный цикл, если для него установлено ожидание
	val, ok := ReadecyclesArr(activedCyckleID)
	if !ok && val != nil {
		if val.isWaitingPeriod == true {
			return false
		}
	}

	// infoFunc31(c) // тестирование

	/* не было стимула от оператора > 10 сек при значительном изменении гомео-параметров
	и при этом ситуация атасная или нужно найти действие

	TODO что-то не срабатывает infoFunc31(c) - нет сочетания условий поэтому сделано еще в func PsychicCountPuls
	*/
	if mentalInfoStruct.noOperatorStimul {
		if isNeedForCommunication() { // нужно провоцировать оператора
			if infoFunc31(c) {
				return true
			}
		}
	} else { // есть стимул, но нет автоматизма

		if mentalInfoStruct.noStaffAtmzmID {
			// в спокойной ситуации
			if !CurrentInformationEnvironment.veryActualSituation && !CurrentInformationEnvironment.danger {

				if isUnrecognizedPhraseFromAtmtzmTreeActivation { //при активации была нераспознанная фраза
					/*Если фраза не понята, то в порядке слов смотреть, на что похожа цепочки слов.
					При выборке сделать массив слов фразы и в каждом кадре - тоже массив слов.
					Это проще, чем распознавание по мере появления новых слов.

					"привет тебе!"
					*/
					if EvolushnStage <= 4 {
						return infoFunc300(c)
					} // для EvolushnStage > 4 есть infoFunc30(c) - Анализировать длинную или недоопределенную фразу, найти обобщающие данные и что-то сделать

				}

			}

		}
	}

	//Нужно подумать о проблеме автоматизма или проявить инициативу, в общем, запустить func infoFunc25()
	if CurrentInformationEnvironment.needThinkingAboutAutomatizm {
		//Более внимательно рассмотреть ситуацию с Правилами: ментальными и связанными с ними моторными
		if !infoFunc29(c) {
			infoFunc25(c)
		}
		return true
	}

	//если есть действие - создать автоматизм infoFunc7() по ID ActionsImage моторного действия (стимула или ответа)
	if mentalInfoStruct.toAutmtzmActionsImageID > 0 {
		//		c.log+="в infoFunc2() ID образа действий, который нужно запустить в автоматизме: "+strconv.Itoa(mentalInfoStruct.toAutmtzmActionsImageID)+"<br>"
		if c.isMainCycle {
			runAutomatizmAfterCheck(c)
		} else {
			insight(c)
		}
		return true
	}
	// ID моторного автоматизма который нужно запустить в infoFunc2()
	if mentalInfoStruct.runMotorAtmzmID > 0 {
		//	c.log+="в infoFunc2() ID моторного автоматизма который нужно запустить: "+strconv.Itoa(mentalInfoStruct.runMotorAtmzmID)+"<br>"
		// подготовить инфу для infoFunc6() из инфы от infoFunc7():
		mentalInfoStruct.motorAtmzmID = mentalInfoStruct.runMotorAtmzmID
		infoFunc6(c) // проверить и если норм. - запустить, сделав штатным
		// на этом и закончить данный цикл
		//endCurrentCyckle()
		return true
	}

	if (curActions.PhraseID != nil && len(curActions.PhraseID) > 1) || isUnrecognizedPhraseFromAtmtzmTreeActivation {
		if EvolushnStage > 4 { //5-я сталия развития. Творчество.
			//Анализировать длинную или недоопределенную фразу, найти обобщающие данные и что-то сделать
			return infoFunc30(c)
		}

	}

	if mentalInfoStruct.epizodFrameIndex > 0 { // было найдено сделующее звено мышления в infoFunc3()
		c.log += "в infoFunc2() было найдено сделующее звено мышления в infoFunc3()<br>"
		// mentalInfoStruct.epizodFrameIndex это - номер инфо-функции
		// нужно перезапустить осмысление в новым шагом
		funcID := mentalInfoStruct.epizodFrameIndex
		mentalInfoStruct.epizodFrameIndex = 0
		mentalInfoStruct.epizodFrameIndex = 0
		if runMentalFunctionID(c, funcID) {
			return true
		}
	}

	/* Если есть mentalInfoStruct.motorAtmzmID > 0 но автоматизм сразу не выполнен,
	   то нужно ПОДВЕРГНУТЬ СОМНЕНИЮ автоматизм и если нормально, то выполнить
	*/
	if mentalInfoStruct.motorAtmzmID > 0 { //
		/*Если заблоикрован mentalInfoStruct.motorAtmzmID и найден новый, то он СРАЗУ ЗАПУСКАТЕСЯ.
		  Если mentalInfoStruct.motorAtmzmID нормальный, то он так же сразу запускается.
		*/
		infoFunc6(c)

		//	c.log+="в infoFunc2() сразу запускается mentalInfoStruct.motorAtmzmID="+strconv.Itoa(mentalInfoStruct.motorAtmzmID)+" <br>"

		mentalInfoStruct.motorAtmzmID = 0
		// на этом закончить данный цикл
		//endCurrentCyckle()
		return true
	}
	/////////////////////////////////

	//!!!! clinerMentalInfo() не терять Тему и Цель!!!!!

	// здесь ищем Какое действие нужно совершить, не только инфо-функции

	if atmtzmActualTreeNode == nil || // нет вообще автоматизма
		atmtzmActualTreeNode.Usefulness < 0 { // запущенный автоматизм заблокирован (м.б. не штатный)
		///////////////////////  поиск решения в мент.Правилах и Моделях понимания
		/* при кажой неудаче предыдушего уровня перезапуск осмыления и более глубокий поиск.
		 */

		/*  ПОИСК РЕШЕНИЯ В МЕНТАЛЬНЫХ ПРАВИЛАХ tryRulesLevel(c,refinementLevel) для данного detectedActiveLastProblemNodID
		Мент.Правила, в конечном счете - последовательность infoFuncSequence []int
				- последовательность ID выполненных инфо-функций одной активации consciousnessElementary()
		Тут нет конкретного образа действия, но есть ID инфо-функций, которые приводили к достижению Цели,
				но С последней ф-цией может быть связан и MentalAutomatizm.motAutmtzmID
				По действию - аналогично func infoFunc3
		*/
		/*funcID := findSuitableMentalFunc()
		if funcID!=nil {
			// выбираем первую функцию TODO подумать
			if runMentalFunctionID(c,funcID[0]){
				return true
			}
		}*/
		fID := findInfoIdFromExperience(true)
		if fID > 0 {
			if runMentalFunctionID(c, fID) {
				return true
			}
		}

		// поиск в Модели понимания
		/*Использовать модели объектов для нахождения действий ПО АНАЛОГИИ с теми действиями,
					что были в эпиз.памяти для данного объекта.
		Леонардо да Винчи смотрел на штукатурку чтобы в голову пришла идея.
		*/
		//
		resM := tryModelsLevel(c)
		if resM { // найдено решение
			c.log += "Удачный поиск в Модели понимания в infoFunc6() будет конец осмысления из-за запуска мот.авто-ма<br>"
			return true
		}
		//}
	}
	//		} //if refinementLevel<6{
	//	}

	// Правила и Модели не сработали, ИДЕМ ДАЛЬШЕ.
	// tryRandExperimtntalAction() дальше неплохо прописано вроде

	/*Далее - быстрое нахождение решения, без сложных размышлений. Размышлять будет когда есть время
	в processingFreeState(stopMentalWork) // как во сне - обработка структур в свободном
	} */
	////////////////////////////////////////

	//	infoFunc13();return true // тестивароние попугайского отзеркаливания

	/* ГЛАВНОЕ - ПРИВЛЕЧЕНИЕ ОСОЗНАННОГО ВНИМАНИЯ к наиболее значимому,
	 есть ли актуальный объект внимания с отрицательной значимостью.
	Информационная функция "Понимание объекта восприятия" - выборка данного образа восприятия в дереве с прослеживанием,
	что оно означало в разных условиях. т.к. образ включает в себя все составляющие объекта восприятия, то он - обобщение,
	а его понимание - Вид всех последствий в разных условиях.
	Для образа фразы type Verbal struct - составляющие отдельные слова, которые могут быть в разных образах фраз,
	так что можно сделать функцию Вида - выборки данного слова в разных условиях с последствиями.
	*/
	if extremImportanceObject != nil {
		// найти способ улучшения значимости объекта extremImportanceObject - запуск моторного автоматизма

		if infoFindAttentionObjImprovement(c) { //  infoFunc9() сделано, но еще не тестировалось
			c.log += "в infoFunc2() найден способ улучшения значимости объекта extremImportanceObject - запуск моторного автоматизма<br>"
			/*при наличии mentalInfoStruct.ActionsImageID > 0
			создать автоматизм (если такого еще нет),
			проверить его и запустить.
			*/
			if c.isMainCycle {
				runAutomatizmAfterCheck(c)
			} else {
				insight(c)
			}
			return true // больше не искать, уже создан мент.автоматизм объективного действия
		}
	}

	if EvolushnStage > 4 { //5-я стадия развития. Творчество.
		/* В тяжелом ментальном состоянии сделать попытку улучшить настроение, эмоцию и ситуацию произвольной переактивацией
		чтобы выйти на решения в лучших условиях. Но нет опасности и можно подумать.
		Произвольная переактивация через func infoFunc14
		*/
		if CurrentMood < 5 && !CurrentInformationEnvironment.danger {
			// прикинуть как переактивировать по состоянию гомеопараметров
			if findReactivadeParams(c) {
				return true // больше не искать
			}
		}

		/*  текущий субъект внимания (наиболее нзачимое в мыслях)
			TODO ЕЩЕ НЕТ ПОДДЕРЖКИ объектов собственных мыслей по аналогии с importance.go И НЕ НУЖНО
			var extremImportanceMentalObject extremImportance
		Для processingFreeState(stopMentalWork) // как во сне - обработка структур в свободном ?
			Здесь должно находиться решение (выбор действия) на основе использования информации
			о наиболее важном в мыслях, скорее всего связанное с Доминантами.

			if infoFindAttentionObjMentalImprovement(){
				return // больше не искать, уже создан мент.автоматизм объективного действия
			}*/
		///////////////////////////////////////

		/*если для данного сочетания Стимул-Ответ есть только один вид эффекта,
		то это - уже Информация, и чем больше опыт (количество обобщенных правил), тем такая информация полезнее.
		infoFunc15()
		*/
		//getDominantEffect(triggerID int, actionID int)

		/*Если оператор нажал кнопку Учитель, это - стимул привлечения внимания для наблюдения за ним,
		за достигаемой им целью и как он ее достигает. Это - начиная с 4-й стадии развития.
		Для понимания отзеркаленных действий оператора цель, которую ставил тварь перед своими действиями PurposeGenetic -
		фксируется в узлах дерева понимания PurposeImage -> SituationImage .
		Кнопки действий теперь активируют смысл (контекст) ситуации SituationImage
		*/
		// TODO

		/*Посмотреть, есть ли другие ветки у данного узла и использовать их начальную инфу.
		В ходе цикла осмысления могут быть переключения на другие условия goNext.MotorBranchID деревьев (перезапуск осмысления)
		при том же контексте Цели (сначала PurposeImage -> SituationImage). Попробовать решения других веток.
		М.б. применить goNextFromUnderstandingNodeIDArr
		В общем - анализ состояния веток циклов осмысления для данной Цели, т.к. кнопки действий активируют смысл (контекст) ситуации SituationImage.
		В том числе, в контексте, если оператор нажал кнопку Учитель, это - стимул привлечения внимания для наблюдения за ним,
		за достигаемой им целью и как он ее достигает.
		*/
		// TODO

	} //if EvolushnStage > 4 { //5-я стадия развития. Творчество.

	/////////////////////////////////////////////////////////
	// СЛУЧАЙНЫЙ ВЫБОР
	needRunMenyalFunc := false // будет запуск инфо-функции,
	funcID := 0
	if EvolushnStage == 4 {
		if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {
			c.log += "в infoFunc2() спросить, как правильно ответить, цикл осмысления прекращается<br>"
			funcID = 13 // спросить, как правильно ответить, цикл осмысления прекращается
		} else { // рискованные эксперименты
			// случайный выбор инфо-функции - поддержка продолжения цикла осмысления
			funcID = infoFindRundomMentalFunction()
		}
		needRunMenyalFunc = true
	}
	if EvolushnStage > 4 {
		//определить, нет ли доминанты с таким объектом, если нет опасности (определяется в checkDominantsJbject)
		if !checkDominantsJbject(extremImportanceObject) {
			if isIdleness() { // ЛЕНЬ
				funcID = 13 // спросить, как правильно ответить, цикл осмысления прекращается
			} else { // рискованные эксперименты
				// случайный выбор инфо-функции - поддержка продолжения цикла осмысления
				c.log += "в infoFunc2() случайный выбор инфо-функции<br>"
				funcID = infoFindRundomMentalFunction()
			}
			needRunMenyalFunc = true
		}
	}

	// завершение шага цикла с записью инфы goNext
	if needRunMenyalFunc {
		if runMentalFunctionID(c, funcID) { // запустить инфо-функцию
			return true
		}
	}

	return false
}

//////////////////////////////////////////////////////////

/*
	№3 найти подходящую infoFunc по опыту ментальных Правил

для данного detectedActiveLastProblemNodID

Тут нет конкретного образа действия, но есть ID инфо-функций, которые приводили к достижению Цели, но
С последней ф-цией может быть связан и MentalAutomatizm.motAutmtzmIDrulesm

Допускается не точное совпадение условий ментального правила.
*/
func infoFunc3(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	/*
		if c.lastFuncID == 3{// не вызывать, если только что было
			return
		}*/
	mentalInfoStruct.epizodFrameIndex = 0
	setCurIfoFuncID(c, 3)
	//	clinerMentalInfo()
	return infoFindRightMentalRules(c)

}
func infoFindRightMentalRules(c *cycleInfo) bool {
	mentalInfoStruct.epizodFrameIndex = 0
	/*
		funcID := findSuitableMentalFunc()
		if funcID!=nil {
		// выбираем первую функцию
			mentalInfoStruct.epizodFrameIndex = funcID[0]
			return true
		}*/
	fID := findInfoIdFromExperience(false) // false - можно не точное совпадение условий правила
	if fID > 0 {
		mentalInfoStruct.epizodFrameIndex = fID
		return true
	}
	return false
}

//////////////////////////////////////////////////////////

/*
	анализ инфо стркутуры mentalInfoStruct и др. информации  и выдача решения.

Нужна таблица, какие инфо-функции вызывать при данной ситуации.
Находит и запускает мот.автоматизм в плохой ситуации
*/
func infoFunc4(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 4 { // раз в прошлый раз уже вызывалась функция infoFunc4() то  не зацикливать
		return false
	}
	setCurIfoFuncID(c, 4)
	//	clinerMentalInfo()
	return analisAndSintez(c.ID, c)

}

/*
	анализ инфо стркутуры и др. информации и выдача решения

Нужна таблица, какие инфо-функции вызывать при данной ситуации??
*/
func analisAndSintez(fromNextID int, c *cycleInfo) bool {
	//sitID:=UnderstandingNodeFromID[detectedActiveLastUnderstandingNodID].SituationID
	unode, oku := ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
	if !oku {
		return false
	}
	sitID := unode.SituationID

	//sit:=SituationImageFromIdArr[sitID].SituationType // текущая ситуация SituationImage
	node, ok := ReadeSituationImageFromIdArr(sitID)
	if !ok {
		return false
	}
	sit := node.SituationType // текущая ситуация SituationImage

	//	theme:=mentalInfoStruct.ThemeImageType // текущая тема Нет тем в Правилах, так что не приплетать

	var stimulsArr = []int{15, 23, 30, 32}

	// псих оценка ситуации: плохо, нужно решать проблему
	var dangAct = 0
	for _, v := range stimulsArr {
		if sit == v {
			dangAct = v
			break
		}
	}
	// псих оценка ситуации: хорошо, можно ничего не делать, даже если действия были опасными (может и не быть такого)
	if sit == 24 || sit == 27 || sit == 31 {
		dangAct = 0
	}
	if dangAct > 0 {
		// выбрать лучшее действие из обозримой эпиз.памяти с хорошим эффектом после стимулов с перечисленными проблемами
		actBestID := 0 // getBestAnswerFromEpisodeStimulsArr(dangAct)
		if actBestID > 0 {
			//			actBest:=ActionsImageArr[actBestID]
			actBest, ok := ReadeActionsImageArr(actBestID)
			if ok {
				/*получить пользу из эффекта лучшего моторного действия
				будет найден и запущен лучший мот.автоматизм aBest.ID
				mentalInfoStruct.motorAtmzmID=aBest.ID
				infoFunc17()// запустить автоматизм и завершить цикл осмысления
				*/
				getBenefitFromEpizosMemory(c, actBest)
				return true
			}
		}
	}

	return false
}

//////////////////////////////////////////////////////////

/*
	№5 Идет цикл мышления об экстремально важном объекте extremImportanceObject

Сновидение или мечтание или пассивное обдумывание или беседа (с кем-то или сам с собою)

// НАПРАВЛЕНИЕ МЫШЛЕНИЯ
Раскрутка сюжета по моторным Правилам со сменой сюжета в направлении наивысшей значимости.

	Теоретически здесь возможно произвольно выбирать глубину детализации объекта внимания ExtremImportanceObjectID
	"символьное" или "образное мышление". А пока что это делается автоматически.

	При выявлении ценного правила возникает озарение,
	прерывание эвристического цикла и начало нового с обновлением в инфо-картине члена ExtremImportanceObjectID.

	Найденное Правило дает новый extremImportanceObject, который там есть и следующий шаг цикла идет по нему.
	Каждый раз в CurrentInformationEnvironment.ExtremImportanceObjectID появляется текущий объект с его значимостью.

	Цель - эвристический синтез новых важных Правил. При этом возникает озарение в виде завершения такого цикла,
	новое правило становится объектом осмысления, extremImportanceObject обнуляется так, что ждет появления нового объекта.
*/
var shotrMemepMemArr []bestActionArr // память на время одного цикла мышления
func infoFunc5(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 5 { // не вызывать, если только что было
		return false
	}

	if c.isWaitingPeriod { // не совершать действий, а ждать ответа
		return false
	}

	if extremImportanceObject == nil { // нет экстремально важного объекта
		return false
	}

	if extremImportanceObjectOld == extremImportanceObject { // уже обрабатывался такой объект
		return false
	}
	extremImportanceObjectOld = extremImportanceObject

	setCurIfoFuncID(c, 5)
	c.log += "Запущена Инфо-функция № 5: Идет цикл мышления об экстремально важном объекте extremImportanceObject ID=" + strconv.Itoa(extremImportanceObject.objID) + "<br>"

	return infoMentalScaning(c)
}
func infoMentalScaning(c *cycleInfo) bool {
	if extremImportanceObject == nil {
		return false
	}

	shotrMemepMemArr = bestActionArrFromExtremImportanceObject()

	// В начале цикла все увиденные по аналогии с extremImportanceObject объекты Модели понимания типа extremImportance
	if shotrMemepMemArr == nil { // еще нет памяти о значащих объектах Модели понимания
		// первичная модель понимания, из топового объекта которой будут возникать другие модели
		// массив ID эпиз памяти для экстр-го объекта
		ActionsImage, effect := bestActionFromExtremImportanceObject()
		if ActionsImage == nil {
			// !!!! extremImportanceObject = nil // все на этом
			return false
		}
		bai := bestActionArr{ActionsImage, effect}
		shotrMemepMemArr = []bestActionArr{bai}
		// с каждым шагом цикла проходим по shotrMemepMemArr, начиная с самых топовых, назнаяая их как extremImportanceObject

		//Выбираем топовый по эффекту элемент shotrMemepMemArr
		eobj := getTopShotrMemepMemArr()
		if eobj != nil {
			extremImportanceObject = eobj
			return true
		}

	} else { // смотрим текущий элемент shotrMemepMemArr ставший extremImportanceObject
		/* Обработка информации об extremImportanceObject может идти по самым разным вариантам, их бесконечное множество.
		Это может стать поистине бесконечным процессом, если после нахождения экстремально важного кадра эпиз.памяти
		пройти по цепочке Правил, создавая по ним новые extremImportanceObject и помещая в стек shotrMemepMemArr[]
		Это бы походило на ветвление природного механизма сновидений у человека
		(но у других видов животных могут совсем другие наследственные алгоритмы).
		Далее реализован упрошенный вариант.
		*/

		// собрать образ действий типа *ActionsImage из элементов с позитивными эффектами цепочки
		sintezAction, ok := createSintezActiveActionsFromExtremImportanceObject()
		if !ok {
			return false
		}
		// добавить новую акцию в опыт
		actionsImageID, _ := CreateNewlastActionsImageID(0, 0, sintezAction.ActID, sintezAction.PhraseID, sintezAction.ToneID, sintezAction.MoodID, true)
		if actionsImageID > prefixActionIdValue { // AmtzmNextString.ID
			// это ID цепочки действия 	AmtzmNextString.ID
			mentalInfoStruct.AmtzmNextStringID = actionsImageID
			//выдать его на пульт
			infoFunc26(c) // запуск с периодом ожидания
			mentalInfoStruct.AmtzmNextStringID = 0
			return true
		} else { // одиночное действие
			if mentalInfoStruct.ActionsImageID > 0 { // создать, проверить и запустить сразу, не в цикле.
				//раз прошли, то просто вырезаем следующий топовый элемент shotrMemepMemArr для продолжения мышления о нем
				//eobj := cutLastExtremObj()
				eobj, _ := getObjectsImportanceValue(mentalInfoStruct.ActionsImageID, detectedActiveLastProblemNodID)
				if eobj != nil {
					extremImportanceObject = eobj
				}
				if c.isMainCycle {
					runAutomatizmAfterCheck(c)
				} else {
					insight(c)
				}
				return true
			}
		}
	}

	//reloadConsciousness(c,mentalInfoStruct.fromNextID)
	return false
}

//////////////////////////////
/* объект extremImportance с максимальным эффектом из стека shotrMemepMemArr[]
Выбираются объекты,
*/
func getTopShotrMemepMemArr() *extremImportance {
	if shotrMemepMemArr == nil {
		return nil
	}

	max := 0
	var maxE *extremImportance
	// вытащить экстрем из массива моделей понимания shotrMemepMemArr
	for i := 0; i < len(shotrMemepMemArr); i++ {

		eobj := getExtremObjFromID(shotrMemepMemArr[i].action.ID)
		if eobj != nil {
			if lib.Abs(max) < lib.Abs(shotrMemepMemArr[i].effect) {
				max = shotrMemepMemArr[i].effect
				maxE = eobj
			}
		}
	}
	if maxE != nil {
		return maxE
	}

	return nil
}

/////////////////////////////////////////////
/*попробовать для случая негативного obj *extremImportance или emExtrem *EpisodeMemory найти прошлый опыт как вывернуться
Из нескольких акций собрать один образ действий типа ActionsImage - из составляющих элементов с позитивными эффектами цепочки Правил
*/
func tryCreteNewActiveActionsFromRulesStr() int {
	sintezAction, ok := createSintezActiveActionsFromExtremImportanceObject()
	if !ok {
		return 0
	}
	// добавить новую акцию в опыт
	aID, _ := CreateNewlastActionsImageID(0, 0, sintezAction.ActID, sintezAction.PhraseID, sintezAction.ToneID, sintezAction.MoodID, true)
	return aID
}

//////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
/* №6 нужно ПОДВЕРГНУТЬ СОМНЕНИЮ автоматизм mentalInfoStruct.motorAtmzmID,
если нет опасности (не нужно реагировать аффектно) и ситуация важна.
Если заблоикрован mentalInfoStruct.motorAtmzmID и найден новый, то он СРАЗУ ЗАПУСКАТЕСЯ.
Если mentalInfoStruct.motorAtmzmID нормальный, то он так же сразу запускается тут.

Найденная по Значимстям и Моделям опасность при запуске будет НАМЕРТВО БЛОКИРОВАТЬ АВТОМАТИЗМ
поэтому в isNextWellEffectFromActonRules() нужно давать возможность преодолевать эту блокирующую значимость
если это приводит в Правилах к победе.

infoFunc6() дает результат сразу, не требуя следующего шага осмысления.
*/
func infoFunc6(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 6 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 6)
	mentalInfoStruct.notOldAutomatizm = false // true - запрет запуска штатного автоматизма mentalInfoStruct.motorAtmzmID
	if (EvolushnStage == 4 || CurrentInformationEnvironment.veryActualSituation) &&
		!CurrentInformationEnvironment.danger {
		if mentalInfoStruct.motorAtmzmID > 0 {
			return infoCreateAndRunNewActionMentAtmzmFromAction(c)
		}
	}
	return false
}
func infoCreateAndRunNewActionMentAtmzmFromAction(c *cycleInfo) bool {

	if mentalInfoStruct.motorAtmzmID == 0 { // ID действия мот.автоматизма, рвущегося на выполнение
		return false
	}
	motorAtmzmID := mentalInfoStruct.motorAtmzmID

	//atmzm,ok := AutomatizmFromId[motorAtmzmID]
	atmzm, ok := ReadeAutomatizmFromId(motorAtmzmID)
	if !ok {
		return false
	}
	actImgID := atmzm.ActionsImageID
	// посмотреть, грозит ли опасностной значимостью запускаемый автоматизм
	res1 := isDangerousImportansAutomatism(actImgID)
	// посмотреть, грозит ли опасностными последствиями в Модели понимания запускаемый автоматизм
	res2 := isDangerousModelAutomatism(actImgID)
	// величины опасной значимости:
	if res1 > 0 || res2 > 0 {
		mentalInfoStruct.motorAtmzmBlockedID = mentalInfoStruct.motorAtmzmID
		/* 	нужно давать возможность произвольно преодолевать эту блокирующую значимость
		если это привоит в Правилах к победе. Сделать ПОИСК В ПРАВИЛАХ !!!!
		*/
		harm := res1
		if res2 > res1 {
			harm = res2
		}
		/* есть ли положительный эффект у Правила, превышающий величину вреда (harm), следующего за действием автоматизма,
		чтобы если он хороший, посчитать такое действие приемлемым и запустить автоматизм?
		*/
		if !isNextWellEffectFromActonRules(3, harm, actImgID) { // нет последующего позитивного эффекта
			mentalInfoStruct.notOldAutomatizm = true // не запускать такой автоматизм
			c.log += "Не запускать старый штатный автоматизм ID=" + strconv.Itoa(actImgID) + "<br>"
			// попробовать найти альтернативу

		}

		/* записать неудачный автоматизм mentalInfoStruct.motorAtmzmBlockedID в массив попыток действия
		и убрать из штатных.
		*/
		if mentalInfoStruct.motorAtmzmBlockedID > 0 {

			//autmzm := AutomatizmFromId[mentalInfoStruct.motorAtmzmBlockedID]
			autmzm, ok := ReadeAutomatizmFromId(mentalInfoStruct.motorAtmzmBlockedID)
			if !ok {
				return false
			}
			SetAutomatizmBelief(autmzm, 0) // убрать из штатных
			addNewTryAction(0, -detectedActiveLastProblemNodID, autmzm.ActionsImageID, -harm, true)
			mentalInfoStruct.motorAtmzmID = 0 // забыть про него
			return false
		}
		mentalInfoStruct.motorAtmzmBlockedID = 0
	} else { // mentalInfoStruct.motorAtmzmID не опасен и сразу тут запускается:
		c.log += "Запустить автоматизм ID=<b><span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + ")'>" + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + "</span></b><br>"
		// вытащить образ действий успешного автоматизма и попробовать решить подходящую по аналогии Домимнату
		checkRelevantAction(curActions.ID, atmzm.ActionsImageID, atmzm.Usefulness)

		infoFunc17(c) // запустить автоматизм, сделав его штатным и завершить цикл осмысления
		mentalInfoStruct.motorAtmzmID = 0
		return true
	}
	return false
}

/*
	сопоставление, грозит ли опасностной значимостью запускаемый автоматизм

Для данных условий
возвращает экстремальную негативную значимость объекта, или 0 если все в порядке
*/
func isDangerousImportansAutomatism(actImgID int) int {

	actImg, ok := ReadeActionsImageArr(actImgID)
	if !ok {
		return -10 // это странная ситуация, что нет данных про actImgID
	}
	objImportance := getExtremObjFromID(actImg.ID)
	if objImportance != nil && objImportance.extremVal < -3 {
		// нет ли потом в правилах этого фрагмента превышающий позитив - нет смысла при большом негативе
		return objImportance.extremVal // вернуть негатив
	}
	return 0
}

//////////////////////////////////////////////////////////
/*  ПОДВЕРГНУТЬ СОМНЕНИЮ автоматизм
используется при быстрой, БЕЗДУМНОЙ проверки штатного автоматизма на первом уровне осмысления.
В основном повторяет проверку в func infoFunc6
Если нет атаса выбирать среди них наиболее подходящий, даже если он не штатный и запускать его (не меняя штатность).
*/
func checkAutomatizm(atmzm *Automatizm) *Automatizm {

	if !CurrentInformationEnvironment.veryActualSituation && !CurrentInformationEnvironment.danger {
		//нет атаса, можно выбрать не штатный автоматизм из привязанных к ветке
		// список всех автоматизмов для ID узла Дерева

		aArr := GetMotorsAutomatizmListFromTreeId(detectedActiveLastNodID)

		harmMax := 1000
		var maxA *Automatizm
		for i := 0; i < len(aArr); i++ {
			actImgID := aArr[i].ActionsImageID
			// посмотреть, грозит ли опасностной значимостью запускаемый автоматизм
			res1 := isDangerousImportansAutomatism(actImgID)
			// посмотреть, грозит ли опасностными последствиями в Модели понимания запускаемый автоматизм
			res2 := isDangerousModelAutomatism(actImgID)
			if res1 > 0 || res2 > 0 {
				harm := res1
				if res2 > res1 {
					harm = res2
				}
				if harmMax > harm {
					harmMax = harm
					maxA = aArr[i]
				}
			}
		}
		if harmMax < 1000 { // выбран самый безопасный
			atmzm = maxA
		}
	}

	if atmzm == nil {
		return nil
	}

	actImgID := atmzm.ActionsImageID
	// посмотреть, грозит ли опасностной значимостью запускаемый автоматизм
	res1 := isDangerousImportansAutomatism(actImgID)
	// посмотреть, грозит ли опасностными последствиями в Модели понимания запускаемый автоматизм
	res2 := isDangerousModelAutomatism(actImgID)
	// величины опасной значимости:
	if res1 > 0 || res2 > 0 {
		harm := res1
		if res2 > res1 {
			harm = res2
		}
		// есть ли положительный эффект у Правила, превышающий величину вреда (harm), следующего за действием автоматизма,
		//чтобы если он хороший, посчитать такое действие приемлемым и запустить автоматизм?

		if !isNextWellEffectFromActonRules(3, harm, actImgID) { // нет последующего позитивного эффекта
			// не запускать такой автоматизм
			// записать неудачный автоматизм mentalInfoStruct.motorAtmzmBlockedID в массив попыток действия
			//и убрать из штатных.
			if mentalInfoStruct.motorAtmzmBlockedID > 0 {

				//	autmzm := AutomatizmFromId[mentalInfoStruct.motorAtmzmBlockedID]
				autmzm, ok := ReadeAutomatizmFromId(mentalInfoStruct.motorAtmzmBlockedID)
				if !ok {
					return nil
				}
				SetAutomatizmBelief(autmzm, 0) // убрать из штатных
				addNewTryAction(0, -detectedActiveLastProblemNodID, autmzm.ActionsImageID, -harm, true)
				// забыть про него
			}
			return nil
		}
		return atmzm
	} else {
		// mentalInfoStruct.motorAtmzmID не опасен и сразу тут запускается:
		// вытащить образ действий успешного автоматизма и попробовать решить подходящую по аналогии Домимнату
		checkRelevantAction(curActions.ID, atmzm.ActionsImageID, atmzm.Usefulness)
		return atmzm
	}
	return nil
}

///////////////////////////////////////////////////////////////////////

/*
	№7 создать новый моторный автоматизм по действию ActionsImageID -

ВСЕГДА ПОСЛЕ ПОЛУЧЕНИЯ ОБРАЗА ДЕЙСТВИЯ mentalInfoStruct.ActionsImageID
Создается моторный автоматизм (если такого еще нет), штатно привязанный к ветке текущей активности дерева автоматизмов.
И по mentalInfoStruct.runMotorAtmzmID=motorID запускается в infoFunc2() -> infoFunc17()
*/
var prevMotorAtmzmID = 0

func infoFunc7(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 7 { // не вызывать, если только что было
		return false
	}
	if mentalInfoStruct.ActionsImageID == 0 { // infoFunc7 не может работать без mentalInfoStruct.ActionsImageID >0
		return false
	}
	setCurIfoFuncID(c, 7)
	if mentalInfoStruct.ActionsImageID > 0 {
		infoCreateAndRunMentMotorAtmzmFromAction(mentalInfoStruct.ActionsImageID, c)
		return true
	}
	return false
}
func infoCreateAndRunMentMotorAtmzmFromAction(ActionsImageID int, c *cycleInfo) bool {
	if ActionsImageID == 0 {
		return false
	}
	motorID, motorAtmzm := createNewAutomatizmID(0, detectedActiveLastNodID, ActionsImageID, true)
	if motorID == 0 {
		mentalInfoStruct.ActionsImageID = 0
		mentalInfoStruct.runMotorAtmzmID = 0
		return false
	}
	if prevMotorAtmzmID == motorID { // раньше был запущен ментально такой мот.автоматизм
		// применить мозжечковый рефлекс
		cerebellumCoordination(motorAtmzm, 1) // 1 - усилить действие
	}
	prevMotorAtmzmID = motorID
	//	clinerMentalInfo()
	//mentalInfoStruct.motorAtmzmID=motorID
	/*if motorID==0{
		mentalInfoStruct.ActionsImageID=0
		mentalInfoStruct.runMotorAtmzmID=0
		return false
	}*/
	// ID моторного автоматизма который нужно запустить в infoFunc2() -> infoFunc17()
	mentalInfoStruct.runMotorAtmzmID = motorID
	return true
}

//////////////////////////////////////////////////////////

/*
	попытаться улучшить значимость текущего объекта extremImportanceObject по моделям понимания (understanding_model.go)

Задача - найти действие в данных условиях (bestActionFromExtremImportanceObject()).
которое лучше, чем текущий extremImportanceObject
и запустить по нему автоматизм, чтобы выработать правило с таким эффектом,
дающее при активации данной ветки автоматизмов более позитивный extremImportanceObject
*/
func infoFunc9(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 9 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 9)
	//	clinerMentalInfo()
	return infoFindAttentionObjImprovement(c)

}

// улучшение значимости объекта внимания
func infoFindAttentionObjImprovement(c *cycleInfo) bool {
	if extremImportanceObject == nil {
		return false
	}

	act, _ := bestActionFromExtremImportanceObject()
	if act != nil {
		//infoFunc2() -> infoFunc7() -> infoFunc17()
		if act.ID > prefixActionIdValue { // последовательность образов действий AmtzmNextString.ID
			mentalInfoStruct.AmtzmNextStringID = act.ID - prefixActionIdValue
			return true
		}
		mentalInfoStruct.ActionsImageID = act.ID //в infoFunc2() создать автоматизм infoFunc7() по ID ActionsImage моторного действия
		return true
	}
	return false
}

////////////////////////////////////////////////////////////////////////

/*
	вспомнить самое значимое, что было - для начала размышения

(кроме возврата на прерванное) по InformationEnvironmentObjects []*InformationEnvironment

Это задает тему для нового цикла осмысления.

Если не найдено подходящей темы, то переактивации не будет.
Если тема найдена - после переактивации м.б. начнется размышление об этом.

TODO Функция может быть значительно усложнена, пока что сделано только самое поверхностное.
*/
func infoFunc10(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 10 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 10)
	return infoFindAttentionObjMentalImprovement(c)
}

// улучшение объекта внимания с произвольной переактивацией дерева
func infoFindAttentionObjMentalImprovement(c *cycleInfo) bool {
	if InformationEnvironmentObjects == nil {
		mentalInfoStruct.volutionReload = 0
		return false
	}
	var needRecalingConsciousness = false
	// посмотреть инфо-окружения назад до первого очень важного объекта
	max := len(InformationEnvironmentObjects)
	for i := max - 1; i >= 0; i-- {
		eo := InformationEnvironmentObjects[i]
		eObj := eo.ExtremImportanceObjectID
		//		obj:=importanceFromID[eObj]
		obj, _ := getObjectsImportanceValue(eObj, detectedActiveLastUnderstandingNodID)
		if obj == nil {
			mentalInfoStruct.volutionReload = 0
			return false
		}
		if obj.extremVal > 3 { // вспомнить хорошее
			if eo.PsyMood > 0 { // хорошее настроение
				createThemeImageID(0, 2, 3, LifeTime, true) //Поисковый интерес
				needRecalingConsciousness = true
			}
			if eo.PsyMood < 0 { // плохое настроение
				createThemeImageID(0, 2, 5, LifeTime, true)
				needRecalingConsciousness = true
			}
		}

		if obj.extremVal < -3 { // вспомнить плохое
			if eo.PsyMood > 0 { // хорошее настроение
				createThemeImageID(0, 3, 17, LifeTime, true) //Состояние Плохо
				needRecalingConsciousness = true
			}
			if eo.PsyMood < 0 { // плохое настроение
				createThemeImageID(0, 3, 3, LifeTime, true) //Улучшение настроения
				needRecalingConsciousness = true
			}
		}

		if needRecalingConsciousness {
			clinerMentalInfo()
			mentalInfoStruct.volutionReload = 1
			// переактивировать с eo.PsyMood и eo.PsyEmotionId
			mentalMoodVolitionID = eo.PsyMood
			mentalEmotionVolitionID = eo.PsyEmotionId
			mentalMoodVolitionPulsCount = PulsCount
			mentalEmotionVolitionPulsCount = PulsCount
			// произвольлная переактивация
			understandingSituation(2)
			return true // не смотреть дальше
		}
	}
	return false
}

//////////////////////////////////////////

/*
	Ментальное отзеркаливание действия оператора

На 4-й стадии нужно отзеркаливать только то, что в действиях оператора достигает поставленной цели.
Щенок наблюдает как спускается по ступенькам и видит, что цель достигнута, начинает сам пробовать так же действовать.

if EvolushnStage == 4{// повышенная вероятность выбора в func infoFindRundomMentalFunction() для 11 и 13 - отзеркаливание
*/
func infoFunc11(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 11 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 11)
	//	clinerMentalInfo()
	return infoMentalMirriring(c)
}
func infoMentalMirriring(c *cycleInfo) bool {
	//есть ли фраза в действиях оператора
	if curActiveActions == nil || curActiveActions.PhraseID == nil {
		return false
	}
	/* алгоритм:
	1. Найти методом GPT такую фразу в Ответах Beast, в Правилах: rulesID
	2. Посмотреть какое последовало действие оператора на это - в эпиз.памяти после rulesID: answer
	3. Создать автоматизм на такое действие и выдать на Пульт.
	*/
	getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
	//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
	//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
	if targetEpisodicStrIdArr != nil {
		// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
		//rules := targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
		acting, ok := ReadeActionsImageArr(targetEpisodicStrIdArr[0].Action)
		if ok {
			mentalInfoStruct.ActionsImageID = acting.ID //в infoFunc2() создать автоматизм infoFunc7() по ID ActionsImage моторного действия
			return true
		}
	}

	mentalInfoStruct.motorAtmzmID = 0
	return false
}

////////////////////////////////////////////////////////////////////////

/*
Cинтез новой фразы из компонентов, имеющих плюсы в Правилах
Возможен при хорошем словарном запасе и хорошем опыте значимости (importance) фраз.

При генерации нового словосочетания в infoFunc12 должна быть запись в дерево фраз?
НЕТ т.к. появляется автоматизм с цепочкой действий-фраз. Не нужен сенсорный детектор такой фразы.
*/
func infoFunc12(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 12 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 12)
	//	clinerMentalInfo()
	return infoSynthesisOwnPrase(c)
}
func infoSynthesisOwnPrase(c *cycleInfo) bool {
	/*сделать фразу, состояющую из 2-3-х известных фраз, найденных в Правилах при данных условиях и выдать ее на Пульт
	  Чтобы фраза не была бессмысленным попугайством, нужно проверять ее смысл по importanceFromID
	  	importance.Type=5//фраза
	    importance.ObjectID=praseID
	    importance.Value>0
	  	для текущих условий
	    importance.NodeAID
	    importance.NodeSID
	*/
	getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
	//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
	//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
	if targetEpisodicStrIdArr != nil {
		// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
		//rules := targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
		var sumPrase []int
		for i := 0; i < len(targetEpisodicStrIdArr); i++ {
			// собираем фразу
			acting, ok := ReadeActionsImageArr(targetEpisodicStrIdArr[i].Action)
			if ok {
				if acting.PhraseID != nil {
					for j := 0; j < len(acting.PhraseID); j++ {
						sumPrase = append(sumPrase, acting.PhraseID[j])
					}
				}
			}
		} // for
		if len(sumPrase) > 0 { // выдать нормальным тоном с настроением wordSensor.CurPultMood
			actID, _ := CreateNewlastActionsImageID(0, 0, nil, sumPrase, 0, wordSensor.CurPultMood, true)
			if actID > 0 {
				//infoFunc2() -> infoFunc7() -> infoFunc17()
				mentalInfoStruct.ActionsImageID = actID //в infoFunc2() создать автоматизм infoFunc7() по ID ActionsImage моторного действия
				return true
			}
		}
	}

	return false
}

/////////////////////////////////////////////////////////////////////////

//TODO: надо переделать логику работы таких функций, работающих в виде цепочек диалога ожидающих ответа на каждом шаге. Это ИНСТИНКТЫ.
// Сделать в виде отдельного потока действий с ожиданием, перехватывающего на себя все внимание. Пока выполняется такой поток-цепочка, никакие функции и циклы не должны встревать, все ждут окончания цепочки либо превышения времения ожидания ответа при шаге цепочки
// в этом случае она прекращается и уступает дорогу другим. Иначе замахаемся отлавливать ситуации, когда работает цикл с func13, и параддельно лезут другие такие же циклы func13
// и разрывают цепочку действий.

/*
	Тупое повторение Стимула оператора. Попугайство.

Показать непонимания, растерянность с вопросом о том, как нужно реагировать.
В отличии от молчаливогоо отзеркаливания в infoFunc11()
создает и использует автоматизм с фразой:
"Ответь сам на "+спопугайничать curActiveActionsID оператора+" чтобы показать, как лучше ответить."
В последствии такой автоматизм может входить в Правила.

Отзеркалить последний Стимул от оператора и совершить такое же действие.
Нужно учесть, что отзеркаливание происходит и в orientation_reflexes.go func orientation_1()!!! м.б. стоит оттуда это убрать?
Отзеркаливает авторитерный ответ Оператора на совершенное действие с помощью func fixNewTeachRules()
- запись авторитарного Правила.

if EvolushnStage == 4{// повышенная вероятность выбора в func infoFindRundomMentalFunction() для 11 и 13 - отзеркаливание
*/
func infoFunc13(c *cycleInfo) bool {
	//	return false // пока блокируем, чтобы не мешалась. Возможно вообще надо убрать
	if c == nil {
		return false
	}
	if isUnrecognizedPhraseFromAtmtzmTreeActivation { // не делать автоматизм из нераспознанной фразы
		return false
	}
	if c.lastFuncID == 13 { // не вызывать, если только что было
		return false
	}
	if curFunc13ID > 0 { // не вызывать, если не отработан второй шаг цикла вопрос-ответ
		return false
	}
	// вызывает лишнюю паузу, два раза бота спрашивать приходится - пока комментим
	//if WasOperatorActiveted==false {// не вызывать, если не было стимула от Оператора
	//	return false
	//}

	if motorActionEffect > 0 {
		return false
	}
	// для случая, когда нажали учительскую кнопку, давшую отрицательный эффект - не нужно запускать в этом случае func 13
	// иначе просто создастся автоматизм с действием кнопки и все
	if GetBelief2AutomatizmListFromTreeId(detectedActiveLastNodID) != nil && mentalInfoStruct.motorAtmzmID > 0 {
		return false
	}

	setCurIfoFuncID(c, 13)
	//	clinerMentalInfo()
	return infoMirroringStimul(c)
}

// маркер для отработки второго шага отзеркаливания после ответа оператора
// в calcAutomatizmResult(): EvolushnStage == 3 || curFunc13ID > 0
var curFunc13ID int

func infoMirroringStimul(c *cycleInfo) bool {
	// свежесть ответа оператора - не позже, чем limitOfActionsAfterStimul пульсов назад
	// число ожидания пульсов должно совпадать с аналогичным числом в RumAutomatizm(), иначе 2-шаговый алгоритм infoFunc13() будет срабатывать не стабильно
	// периодически прерываясь на первом шаге и закрепляя в виде шататного попугайский автотизм, а не авторитарный, который должен помочь создать Оператор
	if curActiveActionsID > 0 {
		if curActiveActionsPulsCount > (PulsCount - limitOfActionsAfterStimul) {

			//actionsImageID,_:=CreateNewlastActionsImageID(0,0,[]int{1,21},nil,0,0,true)
			/* не создавать автоматизм с просьбой показать что-то, а прямо спопугайничть: сделать то же самое, что был 	curActiveActionsID
			в расчете получить реакцию и оценить такой прием как полражание.
			*/
			isTeachQuestion = true //Показать непонимание, растерянность с предложением научить
			motorID, atmz := createNewAutomatizmID(0, detectedActiveLastNodID, curActiveActionsID, true)
			oldUseful := atmz.Usefulness
			wasRunPurposeActionFunc = false // иначе может не пропустить в RumAutomatizmID()
			if atmz.Usefulness < 0 {
				atmz.Usefulness = 0 //если попугайский был заблокирован и получен его код - открываем его, иначе не получится задать вопрос
				atmz.Count = 1
			}
			lib.NoReflexWithAutomatizm = true // не показывать акции рефлексов с автоматизмами в одной плашке действия Beast на пульте
			if RumAutomatizmID(motorID) {
				c.log += "Инфо-функция 13: запуск моторного автоматизма <b> <span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + ")'>" + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + "</span>" + "</b>.<br>"
				runningMotAutmtzmID = motorID
				// при запуске прекратить думать про extremImportanceObject
				//!!! extremImportanceObject=nil
				mentalInfoStruct.motorAtmzmID = 0
				mentalInfoStruct.noStaffAtmzmID = false
				//			wasRunPurposeActionFunc =true  еще не было запуска!
				motorActionEffect = 1

				//setAsMaimCycle(c.ID)

				c.isWaitingPeriod = true    // блокируем повторную активацию этого цикла
				curFunc13ID = c.ID          // создаем маркер активации цикла, закрываем его в calcAutomatizmResult()
				atmz.Usefulness = oldUseful // возвращаем успешность обратно
				return true
			}
			atmz.Usefulness = oldUseful // возвращаем успешность обратно
			isTeachQuestion = false     // подстрахуемся, надо скидывать при любом раскладе - прошел автоматизм или нет
		}
	}
	return false
}

/////////////////////////////////////////////////////////////////////

/*
	Ментально переактиваровать дерево понимания с заданными параметрами узлов,

# НЕ МЕНЯЯ БАЗОВЫЕ КОНТЕКСТЫ

func14 запускается в infoFindRundomMentalFunction() только до 4-й стадии
ИЛИ при if mentalInfoStruct.ThemeImageType == 5 { //Поисковый интерес
т.к. происходит случайный выбор параметров переактивации для набора ментального опыта.
Если не задано mentalInfoStruct.moodeID - то moodeID будет выбрано случайно
Если не задано mentalInfoStruct.emotonID - то выбирается случайно
Если не задано mentalInfoStruct.SituationID - то ситуация выбирается случайно

Функция может использоваться для определенной целевой переактивации TODO,
например, для выхода из плохого ментального состояния (в func infoFunc2).

с mentalInfoStruct.moodeID и mentalInfoStruct.emotonID и mentalInfoStruct.SituationID

При переактивации изменяется Тема и Цель мышления
в func understandingSituation будет перезапущен главный цикл чтобы снова определить тему и цель
*/
func infoFunc14(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 14 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 14)
	// clinerMentalInfo() чтобы можно было задавать mentalInfoStruct.moodeID и mentalInfoStruct.emotonID
	return reactivateEmotionUnderstandingЕree(c)
}
func reactivateEmotionUnderstandingЕree(c *cycleInfo) bool {
	if mentalInfoStruct.moodeID == 0 { // выбрать случайно - для infoFindRundomMentalFunction()
		var infoArr = []int{-1, 0, 1}
		mentalMoodVolitionID = lib.RandChooseIntArr(infoArr)
	} else {
		mentalMoodVolitionID = mentalInfoStruct.moodeID
	}
	if mentalInfoStruct.emotonID == 0 { // выбрать случайно - для infoFindRundomMentalFunction()
		var infoArr []int
		for k, v := range EmotionFromIdArr {
			if v == nil {
				continue
			}
			infoArr = append(infoArr, k)
		}
		mentalEmotionVolitionID = lib.RandChooseIntArr(infoArr)
	} else {
		mentalEmotionVolitionID = mentalInfoStruct.emotonID
	}

	if mentalInfoStruct.SituationID == 0 { // выбрать случайно - для infoFindRundomMentalFunction()
		var infoArr = getSituationTypeArrID()
		mentalSituationVolitionID = lib.RandChooseIntArr(infoArr)
	} else {
		mentalSituationVolitionID = mentalInfoStruct.SituationID
	}
	// это - уже конец главного цикла т.к. ьудет перезапуск understandingSituation(2)
	//wasRunPurposeActionFunc = true   // будет влиять на 1 и 2 уровнях т.к. сбрасывается на 3-м уровне if cycle.count == 0 {
	infoFuncSequence = append(infoFuncSequence, 14) // т.к. более не будет наполняться infoFuncSequence

	mentalMoodVolitionPulsCount = PulsCount
	mentalEmotionVolitionPulsCount = PulsCount
	mentalSituationVolitionPulsCount = PulsCount
	understandingSituation(2)
	clinerMentalInfo()
	return true
}

// переактивировать через func infoFunc14 в лучшее рабочее настроение
func findReactivadeParams(c *cycleInfo) bool {
	mentalInfoStruct.moodeID = 1 // сделать небольшой позитивчик
	// создать подходящую эмоцию
	gPars := gomeostas.GomeostazParams
	var bsIDarr []int
	bsIDarr = append(bsIDarr, 2) // поиск
	if gPars[1] < 30 {
		bsIDarr = append(bsIDarr, 1) // недостаток энергии
	}
	if gPars[8] > 50 { //большие повреждения
		bsIDarr = append(bsIDarr, 5) // защита
	}
	if gPars[4] > 70 && len(bsIDarr) < 3 { // потребность в общении
		bsIDarr = append(bsIDarr, 3) //
	}
	mentalInfoStruct.emotonID, _ = createNewBaseStyle(0, bsIDarr, true)
	// выбрать ситуацию
	mentalInfoStruct.SituationID = 4 //все спокойно, можно экспериментивароть
	if infoFunc14(c) {
		return true // больше не искать
	}
	return false
}

/////////////////////////////////////////////////////////////////////

/*
	для условия дерева автоматизмов (NodeAID) в одиночных Правилах выбираем наилучшее

Eсли для данного сочетания Стимул-Ответ есть только один вид эффекта,

	то это - уже Информация, и чем больше опыт (количество обобщенных правил), тем такая информация полезнее.
*/
func infoFunc15(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 16 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 15)
	//	clinerMentalInfo()
	return beastIDRulesFromCondA(c)
}
func beastIDRulesFromCondA(c *cycleInfo) bool {
	/* В getTargetEpisodicStrIdArr получаем цепочку (или одно правило), заканчивающееся позитивом.
	В цепочке только последний член кончается удачей, остальные отрицательные,
	но смысл цепочки в том, что они приводит к удаче и поеэтому в данных конкретных условиях
	следует выполнить первую акций из этой цепочки.
	*/
	getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
	//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
	//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
	if targetEpisodicStrIdArr != nil {
		// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
		rules := targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
		if rules.Action > 0 {
			//res := makeActionFromRooles(rules) //	!!!!!ПЕРЕДЕЛАТЬ для type Rule struct {
			if rules.Action > prefixActionIdValue { // последовательность образов действий AmtzmNextString.ID
				mentalInfoStruct.AmtzmNextStringID = rules.Action - prefixActionIdValue
				return true
			}
			mentalInfoStruct.ActionsImageID = rules.Action //в infoFunc2() создать автоматизм infoFunc7() по ID ActionsImage моторного действия
			return true
		}
	}

	return false
}

///////////////////////////////////////////////////

/*
	По лучшей ЗНАЧИМОСТИ в данных условиях выдать действие и затем infoFunc7()

и сразу запустить (Писать мент.Правило на случайным действиям бесполезно)
*/
func infoFunc16(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 16 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 16)
	//	clinerMentalInfo()
	return randomAction(c)
}

var oldRandActionsIDarr []int // выполненные действияID, чтобы не повторяться. Сбрасывается при просыпании if IsFirstActivation{
var wasRandPhrase = false     // последний раз была не фраза
func randomAction(c *cycleInfo) bool {
	var rActID = 0 //значимый образ ActionsImage

	if curActiveActions != nil {

	} //if curActiveActions != nil && curActiveActions.PhraseID!= nil{

	// попробовать найти по значимостям
	getBestRuleFromImpotrents()
	if bestRule.Count > 0 { // найден
		if bestRule.Effect == 100 {
			rActID = bestRule.Action
		} else {
			rActID = bestRule.Trigger
		}
	}

	if rActID == 0 {
		// выдать случайное действие из уже известных ActionsImageArr
		var actualArr []int
		for k, v := range ActionsImageArr {
			if v == nil {
				continue
			}
			actualArr = append(actualArr, k)
		}
		rActID = lib.RandChooseIntArr(actualArr)
	}

	if rActID > 0 {
		// не повторяться
		if !lib.ExistsValInArr(oldRandActionsIDarr, rActID) {
			return false
		}

		oldRandActionsIDarr = append(oldRandActionsIDarr, rActID)
		//infoFunc2() -> infoFunc7() -> infoFunc17()
		mentalInfoStruct.ActionsImageID = rActID //в infoFunc2() создать автоматизм infoFunc7() по ID ActionsImage моторного действия
		return true
	}

	return false
}

// /////////////////////////////////////////////////
/* инфа, сопровозжающая ментальный запуск мот.автоматизма НЕ ИСПОЛЬЗУЕТСЯ
var wasMentalRunMotorAtmzmID = 0 // ID моторного автомтаизма, запущенного ментально в infoFunc17()
// при ментальном запуске автоматизма таково состояние extremImportance
var wasExtremImportanceObject *extremImportance
*/
/////////////////////////////////////////////////
/*
	запустить моторный автоматизм mentalInfoStruct.motorAtmzmID

Записать этот автоматизм как штатный для узла дерева автоматизмов.
зафиксировать текущий goNex
и завершить цикл осмысления
*/
func infoFunc17(c *cycleInfo) bool { //запустить моторный автоматизм mentalInfoStruct.motorAtmzmID
	if c == nil {
		return false
	}
	if c.lastFuncID == 17 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 17)
	if mentalInfoStruct.motorAtmzmID == 0 {
		return false
	}

	//autmzm:=AutomatizmFromId[mentalInfoStruct.motorAtmzmID]
	autmzm, ok := ReadeAutomatizmFromId(mentalInfoStruct.motorAtmzmID)
	if !ok {
		return false
	}
	// нет смысла запускать заблокированный автоматизм, его остановят в RumAutomatizmID()
	// и тем более нет смысла делать его штатным
	if autmzm.Usefulness >= 0 {
		SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
	}
	// инфа, сопровозжающая ментальный запуск мот.автоматизма
	//wasMentalRunMotorAtmzmID = mentalInfoStruct.motorAtmzmID
	//wasExtremImportanceObject = extremImportanceObject

	if RumAutomatizmID(mentalInfoStruct.motorAtmzmID) {
		c.log += "Инфо-функция 17: запуск моторного автоматизма <b> <span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + ")'>" + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + "</span>" + "</b>.<br>"
		runningMotAutmtzmID = mentalInfoStruct.motorAtmzmID
		// при запуске прекратить думать про extremImportanceObject
		//!!! extremImportanceObject=nil
		mentalInfoStruct.motorAtmzmID = 0
		mentalInfoStruct.noStaffAtmzmID = false
		lib.NoReflexWithAutomatizm = true // не показывать акции рефлексов с автоматизмами в одной плашке действия Beast на пульте
		wasRunPurposeActionFunc = true    // уже был запущен автоматизм
		// итак успевает
		//infoFuncSequence = append(infoFuncSequence, 17)// т.к. после не будет наполняться infoFuncSequence
		motorActionEffect = autmzm.Usefulness
		levelOfRunAutomatizm = 3 // для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ
		return true
	}

	//по любому остановить цикл т.к. запущен или старый или новый автоматизм
	//endBaseIdCycle(c.ID)
	//	reloadConsciousness(c,0)
	return false
}

/////////////////////////////////////////////////////////////

/*
	Ощущение CurrentInformationEnvironment - по наиболее значимому параметру.

Чем именно грозит наиболее значимое из параметров CurrentInformationEnvironment
1. выбрать  наиболее значимое изменение в параметрах CurrentInformationEnvironment - Здесь есть конкурентность с имеющимся extremImportanceObject (если он есть)
2. найти для него extremImportanceObject
После infoFunc18() всегда перезапуск осмысления
*/
func infoFunc18(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 18 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 18)
	return locForInformationEnvironment(c)
}
func locForInformationEnvironment(c *cycleInfo) bool {

	// конкурентная значимость уже имебщегося объекта внимания extremImportanceObject для сравнения со значимостями пааметров ИнфоОкружения

	var extremVal = 0 // значимость extremImportanceObject если он есть
	var eObj *extremImportance
	// доминанта имеет доминирующий приоритет
	CurrentInformationEnvironment.DominantaID = 0
	if EvolushnStage > 4 {
		if CurrentProblemDominanta != nil {
			dm := CurrentProblemDominanta
			CurrentInformationEnvironment.DominantaID = dm.ID
			extrem0 := dm.weight
			if lib.Abs(extremVal) < lib.Abs(extrem0) {
				extremVal = extrem0
				if dm.objectID > 0 {
					eObj0 := getExtremObjFromID(dm.objectID)
					if eObj0 != nil {
						eObj = eObj0
					}
				}
			}
		}
	}
	//////////////////////////////////////////////////////
	// другие вды значимого
	if CurrentInformationEnvironment.DominantaID == 0 {
		if CurrentInformationEnvironment.ExtremImportanceObjectID > 0 {
			extremObj := getExtremObjFromID(CurrentInformationEnvironment.ExtremImportanceObjectID)
			if extremObj != nil {
				extremVal = extremObj.extremVal
				eObj = extremObj
			}
		}
		// выявить, что изменилось после OldInformationEnvironment
		if CurrentInformationEnvironment.ActionsImageID > 0 && OldInformationEnvironment.ActionsImageID != CurrentInformationEnvironment.ActionsImageID {
			// текущий образ сочетания действий с Пульта
			extrem0 := getExtremEffectFromModel(CurrentInformationEnvironment.ActionsImageID)
			if lib.Abs(extremVal) < lib.Abs(extrem0) {
				extremVal = extrem0
				eObj = &extremImportance{CurrentInformationEnvironment.ActionsImageID, extrem0}
			}
		}
		if OldInformationEnvironment.AnswerImageID != CurrentInformationEnvironment.AnswerImageID {
			// текущий образ сочетания действий с Пульта
			extrem0 := getExtremEffectFromModel(CurrentInformationEnvironment.AnswerImageID)
			if lib.Abs(extremVal) < lib.Abs(extrem0) {
				extremVal = extrem0
				eObj = &extremImportance{CurrentInformationEnvironment.ActionsImageID, extrem0}
			}
		}
	}

	/* Тут могут быть проверки и всех других параметров CurrentInformationEnvironment
	   Их нужно придумать, но УЖЕ ПРИНЦИП осмысления по текущему ИнфоОкружени. ПОКАЗАН.
	   TODO - добавлять сканирования других OldInformationEnvironment.nnn != CurrentInformationEnvironment.nnn по мере необъодимости
	*/

	////////////////////////  наивысший по значимости объект внимания
	if eObj != nil {
		//новый extremImportance
		//addNewcurImportanceObjectArr(*eObj)
		extremImportanceObject = eObj
		c.log += "Выбрана Цель в виде актуального объекта мышления<br> "
	}
	//////////////////////////////////
	/*
		if extremImportanceObject != nil {
			GotoDreaming() // начать сновидение или мечтание(пассивное размышление, не в ответ на Стимул)
			return true
		}
	*/

	return false
}

/////////////////////////////////////////////////////////////////////

/*
	было эвристическое озарение и найдено:

mentalInfoStruct.DominantaID //ID доминанты для коотрой переданы науденные действия
mentalInfoStruct.DominantSuccessAImgID int // найдено успешное действие
mentalInfoStruct.DominantSuccessValue int // оцененная успешность: 1 - доминанта решена, 2 - не точно, 3 - еще менее точно
*/
func infoFunc19(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 19 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 19)
	return thinkingAboutHeuristics(c)
}
func thinkingAboutHeuristics(c *cycleInfo) bool {
	if CurrentInformationEnvironment.DominantaID == mentalInfoStruct.DominantaID {
		// сразу запустить
		if mentalInfoStruct.DominantSuccessValue == 1 {
			mentalInfoStruct.ActionsImageID = mentalInfoStruct.DominantSuccessAImgID
			runAutomatizmAfterCheck(c)
			return true
		}
		if mentalInfoStruct.DominantSuccessValue > 1 {
			// запустить если спосокйно

			return true
		}
	} else {
		/* если нет моторного автоматизма в func consciousnessElementary
		запускать runDominantaAction() (сделано)
		*/
		// при озарении из неглавного цикла (подсознание)
		if mentalInfoStruct.ActionsImageID > 0 {
			runAutomatizmAfterCheck(c)
			return true
		}
	}
	return false
}

/////////////////////////////////////////////////////////////////////////////////////////////

// Воспоминание назад (начиная с dreamingEpisodeHistoryID и уходя в прошлое истории).
/* выбрать последний пустой (т.е. без Правил) кадр эпиз.памяти dreamingEpisodeHistoryID и запустить процесс осмысления infoFunc5()
Чтобы прервать процесс размышления о прошлом:	dreamingEpisodeHistoryID=0
*/
func infoFunc20(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 20 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 20)
	return beginPrepareLastEmptyEpisode(c)
}
func beginPrepareLastEmptyEpisode(c *cycleInfo) bool {

	// последний индекс исторической памяти EpisodicHistoryArr[] но не далее 1000 эпизодов
	if c.dreamingEpisodeHistoryID == 0 {
		c.dreamingEpisodeHistoryID = len(EpisodicHistoryArr) - 1
	}
	lastI := c.dreamingEpisodeHistoryID
	for i := lastI; i >= 0 && i > (lastI-1000); i-- {

		node, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[i].ID)
		if !ok {
			continue
		}
		if node.PARAMS == nil { // на всякий случай, хотя такого не должно быть
			continue
		}
		if node.Action == 0 {
			// вытащить образ Стимула
			//_,ai:=getActiveActionsFromAutomatizmTreeNode(node.NodeAID)
			ai, ok := ReadeActionsImageArr(node.Action)
			// выделить наиболее значимое в восприятии в массив типа extremImportance
			if ok {
				eobj := getExtremObjFromID(ai.ID) // для текущего detectedActiveLastProblemNodID
				if eobj != nil {
					extremImportanceObject = eobj
					if extremImportanceObject.extremVal < -5 { // большой негатив
						//dreamAboutExtremImportanceObject(c) // размышление об extremImportanceObject
						if isDreamInterrupt {
							continue
						}
						if infoFunc5(c) { // при отсуствии extremImportanceObject не пойдет
							continue
						}
					}

					CurrentInformationEnvironment.ExtremImportanceObjectID = extremImportanceObject.objID
					// Можно по extremImportance найти все []*importance: getImportanceFromExtremImportance(Stimul int, kind int)
					// extremImportanceObject.extremVal - НАГАТИВНАЯ ЗНАЧИМОСТЬ объекта
					runNewTheme(16, 2) // при этом создается новый цикл мышления ???

					// i - последний отработанный индекс массива EpisodicHistoryArr []int
					c.dreamingEpisodeHistoryID = i // начать процесс размышления или сновидения
					return true
				}
			}
			break
		}
	}
	c.dreamingEpisodeHistoryID = 0 // прошли до самого начала истории эпиз.памяти
	return false
}

/////////////////////////////////////////////////////////////////////////////////////////////

// Воспоминание вперед (начиная с dreamingEpisodeHistoryID и уходя вперед по истории).
/* выбрать пустой (т.е. без Правил) кадр эпиз.памяти dreamingEpisodeHistoryID
Чтобы прервать процесс размышления:	dreamingEpisodeHistoryID=0
*/
func infoFunc202(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 202 { // не вызывать, если только что было
		return false
	}
	setCurIfoFuncID(c, 202)
	return beginPrepareNextEmptyEpisode(c)
}
func beginPrepareNextEmptyEpisode(c *cycleInfo) bool {
	//	if c.dreamingEpisodeHistoryID == 0 { НАЧИНАТЬ С НАЧАЛА ИСТОРИИ

	lastI := c.dreamingEpisodeHistoryID
	for i := lastI; i < len(EpisodicHistoryArr); i++ {

		node, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[i].ID)
		if !ok {
			continue
		}
		if node.Action > 0 { // пройти назад, чтобы начать рассматривать последовательность кадров с пустого
			i = returnEmptyHistoryID(i)
			n, ok := ReadeEpisodicTreeNodeFromID(EpisodicHistoryArr[i].ID)
			if !ok {
				return false // непонятно что случилось...
			}
			node = n
		}
		if node.PARAMS == nil { // на всякий случай, хотя такого не должно быть
			continue
		}
		if node.Action == 0 {
			// вытащить образ Стимула
			//_,ai:=getActiveActionsFromAutomatizmTreeNode(node.NodeAID)
			ai, ok := ReadeActionsImageArr(node.Action)
			// выделить наиболее значимое в восприятии в массив типа extremImportance
			if ok {
				eobj := getExtremObjFromID(ai.ID) //для текущего detectedActiveLastProblemNodID
				if eobj != nil {
					extremImportanceObject = eobj
					/* это - при новом проходе func GotoDreaming
					if extremImportanceObject.extremVal < -5 { // большой негатив
						dreamAboutExtremImportanceObject(c) // размышление об extremImportanceObject в infoFunc5()
					}
					*/
					CurrentInformationEnvironment.ExtremImportanceObjectID = extremImportanceObject.objID
					// Можно по extremImportance найти все []*importance: getImportanceFromExtremImportance(Stimul int, kind int)
					// extremImportanceObject.extremVal - НАГАТИВНАЯ ЗНАЧИМОСТЬ объекта
					runNewTheme(16, 2) // 16: "Есть объект высокой значимости" при этом создается новый цикл мышления

					// i - последний отработанный индекс массива EpisodicHistoryArr []int
					c.dreamingEpisodeHistoryID = i // чтобы начать следующий проход или сновидения
					return true
				}
			}
			break
		}
	}
	c.dreamingEpisodeHistoryID = 0 // прошли до самого конца истории эпиз.памяти
	return false
}

/////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////
/* Срочно найти какое-то подходящее действие, получить mentalInfoStruct.runMotorAtmzmID
и тут же запустить его
*/
func infoFunc21(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if c.lastFuncID == 21 { // не вызывать, если только что было
		return false
	}
	if motorActionEffect > 0 && !c.dreaming { // не нужно искать решение, раз уже был нормальный ответ
		return false
	}
	setCurIfoFuncID(c, 21)
	return findRunMotorAtmzmID(c)
}
func findRunMotorAtmzmID(c *cycleInfo) bool {

	actionsImageID := findSuitableMotorAction(c)
	if actionsImageID > prefixActionIdValue { // AmtzmNextString.ID
		// это ID цепочки действия 	AmtzmNextString.ID
		mentalInfoStruct.AmtzmNextStringID = actionsImageID
	} else { // одиночное действие
		mentalInfoStruct.ActionsImageID = actionsImageID
	}

	//////////////////  что-нибудь попробовать против опасности
	if mentalInfoStruct.ActionsImageID == 0 {
		if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {

			//  ПАНИКА
			if mentalInfoStruct.ActionsImageID == 0 {
				//создать автоматизм просьбы о помощи из действия ID: 1 (не понимает), 21 (паника)
				//		mentalInfoStruct.ActionsImageID,_=CreateNewlastActionsImageID(0,0,[]int{1,21},nil,0,0,true)
				// вызвать состояние ступора:этот ступорный цикл больше не перезапускать, для ожидания Стимула.
				if infoFunc31(c) {
					return true
				} // сначала попробовать найти решение через провокацию
				c.isStupor = true

				// как бы невербально выражаемая растерянность, не автоматизм, а типа без.рефлекса, но на уровне психики
				lib.SentConfusion("Не могу понять, что делать.<br>СТУПОР!<br>Просьба о помощи.") // непосредственно сообщить о панике
				c.log += "Ступорный цикл остановить. СТУПОР! Просьба о помощи.<br>"
				return true
			}
		}
	} else { // нет опасности
		// TODO что-нибудь попробовать в МЕНТ-Х ПРАВИЛАХ
		if infoFunc3(c) { //найти подходящую infoFunc по опыту ментальных Правил
			runMentalFunctionID(c, mentalInfoStruct.epizodFrameIndex)
			mentalInfoStruct.ActionsImageID = 0
			return true
		}
	}
	// выполнить, если есть что
	if mentalInfoStruct.ActionsImageID > 0 { // создать, проверить и запустить сразу, не в цикле.
		runAutomatizmAfterCheck(c)
		mentalInfoStruct.ActionsImageID = 0
		return true
	}
	if mentalInfoStruct.AmtzmNextStringID > 0 {
		//выдать его на пульт
		infoFunc26(c) // запуск с периодом ожидания
		mentalInfoStruct.AmtzmNextStringID = 0
		return false
	}
	return false
}

/////////////////////////////////
/* срочно найти подходящий образ действия mentalInfoStruct.ActionsImageID
по экстремальному объекту внимания
*/
func findSuitableMotorAction(c *cycleInfo) int {
	actBest, _ := bestActionFromExtremImportanceObject()
	if actBest != nil { // найдено лучшее
		CurrentInformationEnvironment.ActionsImageID = actBest.ID // в текущую инфо-картину
		return actBest.ID
	}

	return 0
}

/////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* Добавление нового действия mentalInfoStruct.ActionsImageID в цепочку действий автоматизма Automatizm.NextID.

Можно в цикле мышления начать формировать произвльную цепочку действий в infoFunc22()
на основе имеющегося автоматизма,
добавляя новое звено, возможно запуская ее по мере добавления нового звена
чтобы оценивать достижение цели с этого звена и с этим звеном.
*/
func infoFunc22(c *cycleInfo) {
	if c == nil {
		return
	}
	// инициативная произвольность возможна только в главном цикле
	if !c.isMainCycle {
		return
	}
	if c.lastFuncID == 22 { // не вызывать, если только что было
		return
	}
	if mentalInfoStruct.ActionsImageID == 0 {
		return
	}
	setCurIfoFuncID(c, 22)
	addNextAtmzmID(c)
}
func addNextAtmzmID(c *cycleInfo) {
	// добавить в конец
	id, _ := addAmtzmNextString(mentalInfoStruct.AmtzmNextStringID, []int{mentalInfoStruct.ActionsImageID})
	if id > 0 {
		mentalInfoStruct.AmtzmNextStringID = id
	}
	//insertImageActionToAmtzmNextID(mentalInfoStruct.ActionsImageID,-1,mentalInfoStruct.motorAtmzmID)
	// запустить mentalInfoStruct.motorAtmzmID с довеском Next
	// НЕТ можно доавбять в цикле, не запуская infoFunc17(c)
}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* Создание новой цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID
с добавлением mentalInfoStruct.ActionsImageID
Просто начать новую произвольную цепочку: mentalInfoStruct.AmtzmNextStringID=0

Можно в цикле мышления начать формировать произвольную цепочку действий mentalInfoStruct.AmtzmNextStringID
не привязанную пока к автоматизму, но которую можно выполнить showNextAtmtzmAction(0,mentalInfoStruct.AmtzmNextStringID,5)
чтобы оценивать достижение цели с этого звена и с этим звеном (непонятно как),
добавляя infoFunc24() новое звено, возможно запуская ее по мере добавления нового звена.
*/
func infoFunc23(c *cycleInfo) {
	if c == nil {
		return
	}
	// инициативная произвольность возможна только в главном цикле
	if !c.isMainCycle {
		return
	}
	if c.lastFuncID == 23 { // не вызывать, если только что было
		return
	}
	if mentalInfoStruct.ActionsImageID == 0 {
		return
	}
	setCurIfoFuncID(c, 23)

	if mentalInfoStruct.ActionsImageID > 0 {
		// добавить в конец
		id, _ := addAmtzmNextString(mentalInfoStruct.AmtzmNextStringID, []int{mentalInfoStruct.ActionsImageID})
		if id > 0 {
			mentalInfoStruct.AmtzmNextStringID = id
		}

		//mentalInfoStruct.AmtzmNextStringID,_=createAmtzmNextStringID(0,[]int{mentalInfoStruct.ActionsImageID},true)
	}

}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* добавление нового звена mentalInfoStruct.motorAtmzmID
в цепочку произвольных действий mentalInfoStruct.AmtzmNextStringID,
возможно запуская ее по мере добавления нового звена infoFunc26()
*/
func infoFunc24(c *cycleInfo) {
	if c == nil {
		return
	}
	// инициативная произвольность возможна только в главном цикле
	if !c.isMainCycle {
		return
	}
	if mentalInfoStruct.ActionsImageID == 0 {
		return
	}
	setCurIfoFuncID(c, 24)
	addVolutionString(c)
}
func addVolutionString(c *cycleInfo) {
	lib.MapFree(MapGwardAutomatizmNextStringFromID)
	nArr, ok := AutomatizmNextStringFromID[mentalInfoStruct.AmtzmNextStringID]
	if !ok {
		return
	}
	insertImageActionToAmtzmNext(nArr, -1, mentalInfoStruct.motorAtmzmID)
}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* Посмотреть условия чтобы проявить инициативу.
СЛУЧАИ:

1. Не было действий Beast  более 100 пульсов, но есть необходимость попробовать что-то сделать (нет другой возможности повлиять на состояние).
Так что ЕСЛИ ФИГОВО И НЕТ АВТОМАТИМА ДЛЯ ДАННОГО СОСТОЯНИЯ (при просыпании проявить инициативу и что-то сделать-сказать).

2. При стимуле с Пульта нет автоматизма или пытается найти альтренативу, в сомнении.
НО Нет походящего Правила, Значимости и Модели чтобы выработать реакцию.
При ИССЛЕДОВАТЕЛЬСКОМ ИЛИ УЧЕНИЧЕСКОМ КОНТЕКСТЕ нужно экспериментировать (иначе - просто промолчать).

В этих случаях запускается функция для 1) найти новое infoFunc23(c)
и запустить как произвольность showNextAtmtzmAction, 2) дополнить сомнительный автоматизм infoFunc22(c),
в том числе если уже есть довесок Automatizm.NextID то добавить еще infoFunc24(c).
*/
func infoFunc25(c *cycleInfo) {
	if c == nil {
		return
	}
	if EvolushnStage < 4 {
		return
	}

	setCurIfoFuncID(c, 25)

	lookForInitiativeAction(c)

	//Нужно подумать о проблеме автоматизма или проявить инициативу, в общем, запустить func infoFunc25()
	CurrentInformationEnvironment.needThinkingAboutAutomatizm = false
}
func lookForInitiativeAction(c *cycleInfo) {

	// для отладки:
	//	CurrentInformationEnvironment.veryActualSituation = true
	//	CurrentInformationEnvironment.IsIdleness100pulse = true

	mentalInfoStruct.AmtzmNextStringID = 0 // если будут найдены действия, то будет создана цепочка с таким ID
	// попробовать найти уже имеющуюся подходящую цепочку для текущей проблемы detectedActiveLastProblemNodID
	//чтобы нарастить ее клон (сохранив оригинал)
	if extremImportanceObject != nil {
		act, _ := bestActionFromExtremImportanceObject()
		if act != nil {
			mentalInfoStruct.AmtzmNextStringID = createClonForActins([]int{act.ID})
			//return
		}
	}

	////////////////////////////////////////s

	//Не было действий Beast  более 100 пульсов, возможно есть условия проявить инициативу
	if CurrentInformationEnvironment.IsIdleness100pulse {
		CurrentInformationEnvironment.IsIdleness100pulse = false // чтобы больше не активировало

		// нужно же что-то сделать, раз нет другой возможности повлиять на состояние
		if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {

			// найти подходящий образ действия по экстремальному объекту внимания
			actionsImageID := findSuitableMotorAction(c)
			if actionsImageID > 0 {
				if actionsImageID > prefixActionIdValue { // AmtzmNextString.ID
					// это ID цепочки действия 	AmtzmNextString.ID
					mentalInfoStruct.AmtzmNextStringID = actionsImageID - prefixActionIdValue
					//выдать его на пульт
					infoFunc26(c) // запуск с периодом ожидания
					mentalInfoStruct.AmtzmNextStringID = 0
					return
				} else { // одиночное действие
					if mentalInfoStruct.ActionsImageID > 0 {
						//Создание новой цепочки произвольных действий с добавлением mentalInfoStruct.motorAtmzmID
						//infoFunc23(c)
						// добавить найденное действие actID в конец существующей цепочки в mentalInfoStruct.AmtzmNextStringID
						addAmtzmNextString(mentalInfoStruct.ActionsImageID, []int{mentalInfoStruct.ActionsImageID})
						mentalInfoStruct.ActionsImageID = 0
						// в опасной ситуации достаточно одного действия, чтобы выдать его на пульт
						infoFunc26(c) // запуск с периодом ожидания
						mentalInfoStruct.AmtzmNextStringID = 0
						return
					}
				}
			}

			// нет действия по экстремальному объекту, ищем в правилах
			/*Найти методом GPT последнее известное Правило по цепочке последних limit кадров эпиз.памяти
			  Найти самое свежее Правило, имеющее схожесть с последними кадрами эпизодической памяти
			  для текущей ветки дерева проблем detectedActiveLastUnderstandingNodID, а значит, для текущей совокупности условий.
			  В эпиз.памяти записываются Правила с учетом последовательности образов действий типа AmtzmNextString.ID
			  				так что функция getRulesFromEpisodicsSlice должна работать корректо.

			  Последний Стимул при активации Дерева автоматизмов: curActiveActionsID, найти для него наиболее преспективную цепочку Правил
			  				и по нему - рекомендуемое действие.
			*/
			getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
			//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
			//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
			if targetEpisodicStrIdArr != nil {
				// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
				rules := targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
				if rules.Action > 0 {
					// найти цепочку AmtzmNextString с таким действием, если нет - создать, если есть - создать клон
					mentalInfoStruct.AmtzmNextStringID = createClonForActins([]int{rules.Action})
				}
			}

		} else { // Не было действий Beast  более 100 пульсов, но неопасная ситуация
			// контекст 2 "Поиск"
			if existsBaseContext(2) {
				var rules Rule // найти Правило
				/*Найти методом GPT последнее известное Правило по цепочке последних limit кадров эпиз.памяти
				  Найти самое свежее Правило, имеющее схожесть с последними кадрами эпизодической памяти
				  для текущей ветки дерева проблем detectedActiveLastUnderstandingNodID, а значит, для текущей совокупности условий.
				  В эпиз.памяти записываются Правила с учетом последовательности образов действий типа AmtzmNextString.ID
				  				так что функция getRulesFromEpisodicsSlice должна работать корректо.

				  Последний Стимул при активации Дерева автоматизмов: curActiveActionsID, найти для него наиболее преспективную цепочку Правил
				  				и по нему - рекомендуемое действие.
				*/
				getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
				//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
				//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
				if targetEpisodicStrIdArr != nil {
					// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
					rules = targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
				}
				if rules.Action > 0 {
					//По правилу найти или создать (в случае AmtzmNextString) автоматизм и запустить его. Если совершено действие - возвращает true
					res := makeActionFromRooles(rules)
					if res {
						return
					}
				}

				// найти подходящий образ действия  в цепочках в спокойной исследовательской ситуации
				findResearchMotorAction(c) // наращена цепочка mentalInfoStruct.AmtzmNextStringID
				if mentalInfoStruct.AmtzmNextStringID > 0 {
					infoFunc26(c) // запуск с периодом ожидания
					mentalInfoStruct.AmtzmNextStringID = 0
					return
				}
			}
		}
	}
	//if CurrentInformationEnvironment.IsIdleness100pulse{

	// набор цепочики mentalInfoStruct.AmtzmNextStringID разными способами
	if mentalInfoStruct.AmtzmNextStringID == 0 {

		// стоит насущная проблема (вызов из func dangerActualProcess)
		lib.MapCheck(MapGwardProblemTryCount)
		if problemTryCount[detectedActiveLastProblemNodID] > 1 { // счетчик нерешенной проблемы для текущего detectedActiveLastProblemNodID
			// найти подходящий образ действия в цепочках в спокойной исследовательской ситуации
			findResearchMotorAction(c) // наращена цепочка mentalInfoStruct.AmtzmNextStringID
		}
		if mentalInfoStruct.AmtzmNextStringID == 0 {
			act := tryCreteNewActiveActionsFromRulesStr()
			// найти цепочку AmtzmNextString с таким действием, если нет - создать, если есть - создать клон
			mentalInfoStruct.AmtzmNextStringID = createClonForActins([]int{act})
		}
	}
	///////////////////////////////////////////

	///////////////////////////////////////////
	if mentalInfoStruct.AmtzmNextStringID > 0 {
		infoFunc26(c) // запуск с периодом ожидания
		mentalInfoStruct.AmtzmNextStringID = 0
		return
	}
	mentalInfoStruct.AmtzmNextStringID = 0

}

//////////////////////////////////////////////////////////////////////

////////////////////////
/* спокойно найти подходящий образ действия mentalInfoStruct.motorAtmzmID в исследовательской ситуации
Выбрать все, что подходит под ситуацию, набирая цепочку
из Правил и из карты AutomatizmNextStringFromID[] (прогнозы по Правилам).

Алгоритм: начиная с текущего CurrentInformationEnvironment.ExtremImportanceObjectID
найти действие, добавить в цепочку, при этом выявляя следующий экстремальный объект
и начинать поиск от него, пока не наберется достаточная (по прогнозам?) цепочка.
*/
func findResearchMotorAction(c *cycleInfo) {

	// по экстремальному образу - м.б. совокупным (сделанным из нескольких позитивных)
	// чтобы найти самый лучший одинчный образ: actBest:=bestActionFromExtremImportanceObject()
	actID := tryCreteNewActiveActionsFromRulesStr()
	if actID != 0 {
		// добавить найденные действия в mentalInfoStruct.AmtzmNextStringID
		addAmtzmNextString(mentalInfoStruct.ActionsImageID, []int{actID})
	}

	// по правилам для текущей ситуации
	getTargetEpisodicStrIdArr(curActiveActionsID, getLimitCountEM())
	//Результат получаем в глобальном targetEpisodicStrIdArr []Rule
	//- окончательно выбранная целевая цепочка Правил откуда берем следующее Действие.
	if targetEpisodicStrIdArr != nil {
		// есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
		rules := targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
		if rules.Action > 0 {
			// добавить найденные действия в mentalInfoStruct.AmtzmNextStringID
			addAmtzmNextString(mentalInfoStruct.ActionsImageID, []int{rules.Action})
		}
	}
	return
}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* Запуск на выполнение на пульте имеющейся цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID
без создания автоматизма
с периодом ожидания после которого автоматизм создается по результату гомео-эффекта.
*/
func infoFunc26(c *cycleInfo) {
	if c == nil {
		return
	}
	// инициативная произвольность возможна только в главном цикле
	if !c.isMainCycle {
		return
	}
	if mentalInfoStruct.AmtzmNextStringID == 0 {
		return
	}
	setCurIfoFuncID(c, 26)
	if LastRunAutomatizmPulsCount > 0 { // не запускать в период ожидания
		return
	}
	runVolutionString(c)
}
func runVolutionString(c *cycleInfo) {
	levelOfRunAutomatizm = 3 // для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ

	infoFuncSequence = append(infoFuncSequence, 26) // т.к. после не будет наполняться infoFuncSequence

	// запуск с энергией == 5
	showNextAtmtzmAction(0, mentalInfoStruct.AmtzmNextStringID, 5)

	lib.NoReflexWithAutomatizm = true // не показывать акции рефлексов с автоматизмами в одной плашке действия Beast на пульте
	wasRunPurposeActionFunc = true    // уже была запущена цепочка действий
}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* Создание автоматизма на основе имеющейся цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID
Получение значения mentalInfoStruct.motorAtmzmID для запуска.

Эта функция - на всякий случай, т.к.
АВТОМАТИЗМ ВСЕГДА СОЗДАЕТСЯ ПРИ ЗАПУСКЕ mentalInfoStruct.AmtzmNextStringID на выполнение
	по результату периода ожидания - в func calcAutomatizmResult()
*/
func infoFunc27(c *cycleInfo) {
	if c == nil {
		return
	}
	// инициативная произвольность возможна только в главном цикле
	if !c.isMainCycle {
		return
	}
	if mentalInfoStruct.AmtzmNextStringID == 0 {
		return
	}
	setCurIfoFuncID(c, 27)
	createAnvtzmFromVolutionString(c)
}
func createAnvtzmFromVolutionString(c *cycleInfo) {
	automatizm := createAndRunAutomatizmFromAmtzmNextString(mentalInfoStruct.AmtzmNextStringID)
	mentalInfoStruct.motorAtmzmID = automatizm.ID
}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* фантазия - как функция интенсивного подбора ассоциаций для решения доминанты

Это - гипотетичкая реализация, - намеки на будущую, задел.

В результате - // НЕТ получение цепочки произвольных действий mentalInfoStruct.AmtzmNextStringID ????
- получение значения actionsImageID.

Эта функция - на всякий случай, т.к.
АВТОМАТИЗМ ВСЕГДА СОЗДАЕТСЯ ПРИ ЗАПУСКЕ mentalInfoStruct.AmtzmNextStringID на выполнение
	по результату периода ожидания - в func calcAutomatizmResult()

При каждом проходе генерировать 1 ассоциацию и примерять ее к актуальной доминанте.
*/
func infoFunc28(c *cycleInfo) {
	if c == nil {
		return
	}
	// инициативная произвольность возможна только в главном цикле
	if !c.isMainCycle {
		return
	}
	if mentalInfoStruct.AmtzmNextStringID == 0 {
		return
	}
	setCurIfoFuncID(c, 28)
	createAnvtzmFromVolutionString(c)
}
func fantasing(c *cycleInfo) {

	var mainDominanta *Dominanta
	// выбрать наиболее актуальную для detectedActiveLastProblemNodID доминанту
	maxWeight := 0 // пока выбор по весу, но нужно по detectedActiveLastProblemNodID
	for _, v := range DominantaProblem {
		if v == nil {
			continue
		}
		if v.weight > maxWeight {
			maxWeight = v.weight
			mainDominanta = v
		}
	}

	if maxWeight == 0 {
		return
	}
	CurrentProblemDominanta = mainDominanta

	effect := 1          // предположительный эффект
	actionsImageID := 0  // найденное действие для ассоциации с доминантами
	actionSucxessID := 0 // найденное действие успешного автоматизма, имеющего actionsImageID
	// в принципе actionsImageID == actionSucxessID

	/* генерация ассоцияций
	   ??? нужно бы учитывать: getCurSituation(), getCurTheme() и getCurPurpose()
	*/
	limit := 20
	n := 0
	for {
		actionsImageID = associationGeneration()
		if actionsImageID > 0 {
			// проверка, насколько данная ассоциаяция подходит для целей CurrentProblemDominanta
			if chechDominantPopose() {
				break
			}
		}
		if n > limit {
			return
		}
		n++
		continue
	}

	//попробовать решить подходящую по аналогии Домимнату с предположительным эффектом==effect
	res := checkRelevantAction(actionsImageID, curActions.ID, effect)
	if res > 1 {
		/* Эвристически озариться найденным решением
		success - 1 - доминанта решена, 2 - не точно, 3 - еще менее точно
		*/

		toConsciousHeuristics(actionsImageID, actionSucxessID, CurrentProblemDominanta)
	}
}

/*
	сгенерировать ассоциацию в виде ID действия

Правила - только один из механизмов поиска ассоциаций (и тут может применять прогноз по групповым правилам).
Причем использование своих правил - одно, а учительских - даст другое.
Другим способом нахождения ассоциаций м.б. перебор моделей значимых объектов,
чтобы отзеркалить действия, совпадающие с целью доминанты.
Ну и сама выборка правил м.б. на основе значимостей объектов, далеких от текущей ветки дерева,
но отвечающих цели.
*/
var oldAssociationsArr []*ActionsImage // не генерировать уже проверенные ассоциации
func associationGeneration() int {

	// TODO

	return 0
}

// проверка, насколько данная ассоциаяция подходит для целей CurrentProblemDominanta
func chechDominantPopose() bool {

	// TODO
	return true
}

///////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* Более внимательно рассмотреть ситуацию с Правилами: ментальными и связанными с ними моторными.
В результате найти полезное действие и
*/
func infoFunc29(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if !c.isMainCycle {
		return false
	}
	setCurIfoFuncID(c, 29)

	return lookForRulesMentalAndMotor(c)
}
func lookForRulesMentalAndMotor(c *cycleInfo) bool {
	actBestID := 0
	// найти кадры ментальной эпиз.памяти для данных условий с позитивным эффектом
	NodePID := detectedActiveLastProblemNodID // ID проблемы
	ThemeID := problemTreeInfo.themeID
	PurposeID := mentalInfoStruct.mentalPurposeID
	getEpisodesMentalArrFromConditions(NodePID, ThemeID, PurposeID, 1)
	if ePMentSrsIdArr == nil {
		return false
	}
	/* найти кадры моторной эпиз.памяти, связанные с ментальными по времени создания
	и сопоставить их
	*/
	for i := 0; i < len(ePMentSrsIdArr); i++ {
		motorID := EpisodicMentalHistoryArr[ePMentSrsIdArr[i]].lastEpisodicMemID
		if motorID > 0 {
			motorEP, ok := ReadeEpisodicTreeNodeFromID(motorID)
			if ok && motorEP != nil {

				// TODO придумать как сопоставить и найти лучший вариант actBestID
			}
		}
	}
	////////////////////////////////////////////

	if actBestID > 0 {
		actBest, ok := ReadeActionsImageArr(actBestID)
		if ok {
			/*получить пользу из эффекта лучшего моторного действия
			будет найден и запущен лучший мот.автоматизм aBest.ID
			mentalInfoStruct.motorAtmzmID=aBest.ID
			infoFunc17()// запустить автоматизм и завершить цикл осмысления
			*/
			getBenefitFromEpizosMemory(c, actBest)
			return true
		}
	}
	return false
}

/////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/* найти в неопределенной фразе что-то ранее понятное и выполнить действия
 */
func infoFunc300(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if !c.isMainCycle {
		return false
	}
	setCurIfoFuncID(c, 300)

	actsID := 0 // ищем эффективную акцию

	// CurretWordsIDarr - массив словID и вместо нераспознанного -1
	if len(wordSensor.CurretWordsIDarr) == 0 {
		return false
	}
	wArr := wordSensor.CurretWordsIDarr

	/* найти слово в стимуле правил для текущих условий и более общих, но не во всей эп.памяти
	оценивая значимость стимула с таким словом и игнорируя малую значимость.
	Вернуть акцию наиболее успешного правила
	*/
	actsID = getBestActionFromWordID(wArr)

	if actsID == 0 {
		wordsModel := getWordArrModelExactly(wArr)
		if wordsModel.beastFrameID > 0 { // ID наилучшего по эффекту кадра памяти
			actionNode, ok := ReadeEpisodicTreeNodeFromID(wordsModel.beastFrameID)
			if ok {
				actsID = actionNode.Action
			}
		}
	}

	/*  если бы был набор фраз, то можно было бы так:
	if isUnrecognizedPhraseFromAtmtzmTreeActivation && curActions.PhraseID != nil && len(curActions.PhraseID) > 1 {
			actsID := prepareUnrecognizedPhrase(curActions.PhraseID) // найти в неопределенной фразе что-то ранее понятное и выполнить действия

		}
	*/
	if actsID > 0 { // ID действия с наиболее позитивным Эффектом
		mentalInfoStruct.ActionsImageID = actsID
		runAutomatizmAfterCheck(c)
		isUnrecognizedPhraseFromAtmtzmTreeActivation = false
		return true
	}
	return false
}

/////////////////////////////////////////////////////////////////////
/* Анализировать длинную или недоопредеелнную фразу и найти обобщающие данные
 */
func infoFunc30(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	if !c.isMainCycle {
		return false
	}
	setCurIfoFuncID(c, 30)

	if curActions.PhraseID != nil && len(curActions.PhraseID) > 1 {
		return preparelongPhrase(curActions.PhraseID) // TODO действия по найденным данным
	}
	// нараспознанная фраза
	if isUnrecognizedPhraseFromAtmtzmTreeActivation {
		return preparelongPhrase(curActions.PhraseID) // TODO действия по найденным данным
	}

	return false
}

// ///////////////////////////////////////////////////////////
/* Провокационные действия, когда нет стимула от оператора, но нужно что-то делать
не было стимула от оператора > 10 сек при значительном изменении гомео-параметров
*/
func infoFunc31(c *cycleInfo) bool {
	// !!! infoFunc31pulsCount = 0
	if c == nil {
		return false
	}
	if !c.isMainCycle {
		return false
	}
	idleness := isIdleness() // лень?
	if IsSleeping || !c.dreaming || idleness {
		return false
	}
	setCurIfoFuncID(c, 31)

	//if wasRunTreeStandardAutomatizm { // уже был запущен штатный автоматизм после Стимула.
	if LastRunAutomatizmPulsCount > 0 { // уже был запущен штатный автоматизм после Стимула - период ожидания
		return false
	}
	if wasRunPurposeActionFunc { // если ранее был запущен ментально в infoFunc17
		return false
	}

	// wasRunProvocationFunc используется как флаг "провокация func infoFunc31"
	wasRunProvocationFunc = true //сработала провокация оператора на действие, очистка - в clinerAutomatizmRunning()
	/*Выбрать лучшее Правило, как можно с более точным учетом условий, искать все виды Правил
	т.к. поиск идет при Стимуле==0, то может находится много правил, из них выбирается самое эффективное.
	При ответе оператора на провокацию будет записано Правила со Стимулом равным 0
	что можно использовать как выбор или избегание такого действия, но с учетом условия - действие без стимула опрератора.
	*/
	rule := getProvocationBestRule()
	if rule.Action > 0 {
		//по правилу найти или создать (в случае AmtzmNextString) автоматизм и запустить его
		//res := makeActionFromRooles(rule)
		ai, ok := ReadeActionsImageArr(rule.Action)
		if ok {
			purpose := getPurposeGenetic()
			purpose.actionID = ai
			levelOfRunAutomatizm = 3 // для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ
			createAndRunAutomatizmFromPurpose(purpose)
			if !isInterruptAutmtzm { // не выполнился, например если уже был запущен штатный автоматизм после Стимула и т.п.
				wasRunPurposeActionFunc = false //иначе не пропускает на исполнение
				// судя по всему, происходит наложение активации lib.SentActionsForPult(out) от рефлексов и автоматизмов, и выдает действия обоих.
				lib.NoReflexWithAutomatizm = true // не показывать акции рефлексов с автоматизмами в одной плашке действия Beast на пульте
				//action_sensor.DeactivationTriggers()
				//notAllowReflexRuning = true
				return true
			} else {
				wasRunProvocationFunc = false // раз автоматизм не запустился
			}
		}
	}

	return false
}

/////////////////////////////////////////////////////
//
//
//
//    КОНЕЦ ТЕЛ ИНФО_ФУНКЦИЙ
////////////////////////////////////////////////////////

/////////////// ПОДДЕРЖКА ИНФО_ФУНКЦИЙ ////////////////////////////////////////////////

// озарение при найденном решении в неглавном цикле (подсознании)
func insight(c *cycleInfo) {

	infoFunc19(c) //- было эвристическое озарение

	setAsMaimCycle(c.ID) //	сделать цикл главным
}

////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////
/* при наличии mentalInfoStruct.ActionsImageID > 0
создать автоматизм (если такого еще нет),
проверить его и запустить.
*/
func runAutomatizmAfterCheck(c *cycleInfo) {
	if mentalInfoStruct.ActionsImageID > 0 { // создать, проверить и запустить сразу, не в цикле.
		c.log += "По образу действий создать автоматизм, проверить его и запустить.<br>"
		if infoFunc7(c) { //создать новый моторный автоматизм по действию ActionsImageID
			// подготовить инфу для infoFunc6() из инфы от infoFunc7():
			mentalInfoStruct.motorAtmzmID = mentalInfoStruct.runMotorAtmzmID
			infoFunc6(c) // проверить и если норм. - запустить, сделав штатным
		}
	}
}

/////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
/* случайный выбор ментальной функции, из тех, что еще не использовались в данном цикле (нет в infoFuncSequence)

В infoFuncSequence сохраняются активированные инфоID так что можно не вызывать то, что уже прошло в цикле.

TODO СЛУЧАЙНОЕ ДЕЙСТВИЕ НУЖНО ОГРАНИЧИВАТЬ ТЕМОЙ И ЦЕЛЬЮ!
*/
func infoFindRundomMentalFunction() int {
	//	return 13 // тестирование

	if transfer.IsPsychicТeachingMode {
		return 13
	}

	switch mentalInfoStruct.ThemeImageType {
	case 3: // Состояние Плохо
		return 122
	case 6: // Обучение с учителем
		return 13
	}

	clinerMentalInfo() // чтобы было случайное clinerMentalInfo() для 14-й функции

	// 11 и 13	отзеркаливание
	// весь набор допустимых функций повышенная вероятность для 13 и пониженная для 14
	var infoArr = []int{4, 9, 11, 11, 11, 12, 13, 13, 13, 14, 15, 16} // повышенная вероятность для 11 и 13  - отзеркаливание
	if EvolushnStage > 4 {                                            // повышенная вероятность для 11 - отзеркаливание
		infoArr = []int{4, 9, 11, 11, 11, 12, 15, 16}
	}
	if mentalInfoStruct.ThemeImageType == 5 { //Поисковый интерес
		infoArr = []int{11, 12, 14}
	}

	/* var actualArr []int
	чтобы func13 прошла нужно удалить ее код из functionsInAllCickles. Блокировка вызова 2 экземпляров делается через curFunc13ID
	иначе в functionsInAllCickles добавится 13 от какого то цикла и больше не даст запустить отзеркаливание не явным образом
	clinerFunctionsInAllCickles(13)
		for i := 0; i < len(infoArr); i++ {
			if !lib.ExistsValInArr(functionsInAllCickles, infoArr[i]) {
				actualArr = append(actualArr, infoArr[i])
			}
		}*/
	actualArr := listUnusedInfoId(infoArr) // только еще не использованные
	if actualArr != nil && len(actualArr) > 0 {
		funcID := lib.RandChooseIntArr(actualArr)
		/*не вызывалась ли такая случайная функция funcID в цикле? чтобы не вызывать подряд
		exists := lib.ExistsValInArr(functionsInAllCickles, funcID)
		var tryN = 20
		for exists && tryN > 0 {
			funcID := lib.RandChooseIntArr(actualArr)
			exists = lib.ExistsValInArr(functionsInAllCickles, funcID)
			tryN--
		}
		if !exists {*/
		if funcID > 0 {
			return funcID
		}
	}
	return 0
}

// список функций, котоорые еще не были использованы
func listUnusedInfoId(list []int) []int {
	var out []int
	for i := 0; i < len(list); i++ {
		if !lib.ExistsValInArr(infoFuncSequence, list[i]) {
			out = append(out, list[i])
		}
	}
	return out
}

////////////////////////////////////////////////

/* чтобы после отработки func13 infoFindRundomMentalFunction позволяло выдать новый код 13
нужно удалить из functionsInAllCickles коды прошлых вызовов 13
func clinerFunctionsInAllCickles(val int) {
	for id, n := range functionsInAllCickles {
		if n == val {
			functionsInAllCickles = lib.RemoveArrIndex(functionsInAllCickles, id)
			break
		}
	}
}*/

/*
*************** Группировка зеркальных автоматизмов *********************
Были зафиксированы две цепочки диалога по 2 шага каждая (1 шаг - 1 пара вопрос + ответ):
а) Привет – привет, как дела – нормально
б) Приветствую – привет, Ты как – отлично
Из них сформировались 2 зеркальных автоматизма: привет - как дела, привет - ты как. Их можно сгруппировать и вывести в отдельном
массиве варианты ответов на один пускатель: привет - как дела, ты как.
При поиске ответа нужно искать в этом массиве и выбирать варианты например по счетчику успешности. Это будет намного быстрее,
чем перебирать весь массив автоматизмов.
Для такой группировки нужно при создании нового зеркального автоматизма дописывать
в этот массив новый вариант в нужной строке: находить в массиве пусковое слово и добавлять к нему вариант ответа.
По сути это групповой запрос, только выделенный в динамическую таблицу. Так БД-шники часто делают, если приходится ворочать объемные
данные под миллионы записей. Вместо тяжелых тормозных запросов строятся буферные таблицы и забиваются через хранимки при совершении
операций с данными.
*/

//////////////////////////////////////////////////

///////////////////////////////////////////////////////

// при запуске любой инфо-функций - общие установки
func setCurIfoFuncID(c *cycleInfo, infofID int) {
	if c == nil {
		return
	}
	if infofID == 0 {
		return
	}
	// набор инфоID для func saveNewMentalEpisodic
	if !wasRunPurposeActionFunc { //После запуска автоматизма прекратить набор кадров и ждать ответа
		infoFuncSequence = append(infoFuncSequence, infofID)
	}

	c.lastFuncID = infofID
	c.func0Arr = append(c.func0Arr, infofID)
	c.funcArr = append(c.funcArr, infofID)

	if !lib.ExistsValInArr(functionsInAllCickles, infofID) {
		functionsInAllCickles = append(functionsInAllCickles, infofID)
	}
	// В лог - если это не функции из списка:
	if infofID != 1 && infofID != 2 && infofID != 5 && infofID != 8 && infofID != 17 {
		fName := getMentalFunctionString(infofID)
		if !c.idle && !c.isMainCycle || show_all_logs {
			c.log += "Запущена Инфо-функция № " + strconv.Itoa(infofID) + ": <i>\"" + fName + "\"</i><br>"
		}
	}

	if infofID == 17 {
		// вывод в функции
	}
	if infofID == 1 {
		//c.log+="Завершен цикл мышления.<br>"
	}

}

//////////////////////

/*
	Прдотвращние постоянного повторения ряда инфо-функций.

Если в цикле funcArr начинает повторяться последовательность выовов infofID,
то нужно пропустить вызов infofID начала такоей последовательности.
Последний фрагмент cycle.funcArr всегда == cycle.func0Arr
*/
func correctRepeatCycleFuncStr(cycle *cycleInfo) bool {
	if cycle.func0Arr == nil {
		return false
	}
	a0Arr := cycle.func0Arr
	aArr := cycle.funcArr
	len0F := len(a0Arr)
	lenF := len(aArr)
	// есть ли в предшествующем фрагменте cycle.funcArr повторение cycle.func0Arr
	if lenF > 2*len0F {
		// предшествующй фрагмент cycle.funcArr
		fArr := aArr[:(lenF - len0F)]
		prevF := fArr[(len(fArr) - len0F):]
		if lib.EqualArrs(prevF, a0Arr) {
			return true
		}
	}
	return false
}

// ////////////////////////////////////////////////////////////////////////
var infoFunc31pulsCount = 0
var waitingTimeBeforeProvocation = 50 // сколько ждет пульсов до следующей попытки провокации
// нужно провоцировать оператора
func isNeedForCommunication() bool {

	if infoFunc31pulsCount > 0 && (PulsCount-infoFunc31pulsCount) < 30 {
		// уже была провокация
		return false
	}
	if (PulsCount - curActiveActionsPulsCount) > waitingTimeBeforeProvocation { // прошло > 10 пульсов со времени последнего стимула от оператора
		if CurrentInformationEnvironment.veryActualSituation ||
			CurrentInformationEnvironment.danger ||
			CurrentInformationEnvironment.needThinkingAboutAutomatizm ||
			gomeostas.IsNeedForCommunication() {
			infoFunc31pulsCount = PulsCount
			return true
		}
	}
	return false
}

///////////////////////////////////////////////////////////////////////////

/*
получить пользу из эффекта лучшего моторного действия
будет найден и запущен лучший мот.автоматизм aBest.ID
mentalInfoStruct.motorAtmzmID=aBest.ID
infoFunc17()// запустить автоматизм и завершить цикл осмысления
*/
func getBenefitFromEpizosMemory(c *cycleInfo, actBest *ActionsImage) bool {
	if detectedActiveLastNodID == 0 { // так не должно быть..
		return false
	}
	// может быть несколько автоматизмов с одним и тем же действием, но с разными BranchID веками дерева автоматизмов или вообще не привязанные
	var eMax = 0
	var aBest *Automatizm

	//штатный автоматизм активного узла дерева
	sA := AutomatizmBelief2FromTreeNodeId[detectedActiveLastNodID]
	if sA != nil {
		if sA.ActionsImageID == actBest.ID {
			// как раз штатный автоматизм и делает это
			mentalInfoStruct.motorAtmzmID = sA.ID
			infoFunc17(c) // запустить автоматизм и завершить цикл осмысления
			return true
		}
	}

	//найти мот.автоматизмы с таким действием и сравинить со штатным автоматизмом активного узла detectedActiveLastNodID

	for _, v := range AutomatizmFromId {
		if v == nil {
			continue
		}
		if v.ActionsImageID != actBest.ID || v.Usefulness <= 0 {
			continue
		}
		if eMax < v.Usefulness {
			eMax = v.Usefulness
			aBest = v
		}
	}

	if aBest != nil {
		mentalInfoStruct.motorAtmzmID = aBest.ID
		infoFunc17(c) // запустить автоматизм и завершить цикл осмысления
		return true
	} else { // нет мот.автоматизм в конечном звене цикла, создать такой по действию actBest.ID
		//в infoFunc2() -> infoFunc7() -> infoFunc17()
		mentalInfoStruct.toAutmtzmActionsImageID = actBest.ID
	}

	return false
}

////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/mental_automatizm_INFO_functions_Purpose.go
____________________________________________
/* Инфо-функция поиска Цели - как объекта произвольного внимания:
того из всего воспринимаемого, что имеет наибольшую значимость
т.к. именно наибольшая значимость должна осмысливаться.

targetID [] можно пока вообще не трогать, так же как и veryActual -
оба просто констатируют что нужно бы улучшить.

Тут главное - определить PurposeImage.actionID -
выбранный образ действия бота для достижения данной цели (ActionsImage)

т.е. если не определно произвольное PurposeImage.actionID или вообще не задана Цель,
то целью автоматически становится улучшение состояния, см. func valuationPurpose().

На пятой ступени развития целью может стать решение Доминанты нерешенной проблемы, и только
если нет Доминант, то работает неосознаваемая Цель mentalInfoStruct.mentalPurposeID.
*/

package psychic

import (
	"strconv"
)

//////////////////////////////////////////

//////////////////////////////////////////////////////////
/* Ментальное определение ближайшей Цели в данной ситуации по теме в getMentalPurpose()
Для цикла мышления с новой темой mentalInfoStruct.ThemeImageType
Постановка цели для текущего цикла размышления, чтобы оценить эффект для мент.Правила.
*/
var newCycklePulsCount = 0 //время запуска infoFunc8()
/*
// переменные только дял этой функции:
var oldThemeID8=0
var oldPurposeID8=0
func initInfoFunc8pars(){// с каждым ориентировочным рефлексом функция циклов осмысления func consciousnessElementary()

		oldThemeID8=0
		oldPurposeID8=0
	}
*/
func infoFunc8(c *cycleInfo) bool {
	if c == nil {
		return false
	}
	//  цель может модифицировать в цикле, но не подряд!
	//!!!! может быть подряд! if c.lastFuncID == 8 { // не вызывать, если только что было
	//	return false
	//}
	setCurIfoFuncID(c, 8)

	//if newCycklePulsCount==PulsCount{// от случайных повторых запусков МОЖЕТ В ОДНОМ ЦИКЛЕ СМЕНИТЬСЯ ТЕМА И ЦЕЛЬ!
	/* проверяется использование infoFunc8 в течение жизни цикла, чтобы не молотил зря:
	   если не менялась тема и цель
	   Даже если не было других инфо-фукнций после 8-й, но тема или цель менялись, то нужно отработать infoFunc8 снова.

	   	if problemTreeInfo.themeID>0 && mentalInfoStruct.mentalPurposeID>0 &&
	   	oldThemeID8==problemTreeInfo.themeID && oldPurposeID8==mentalInfoStruct.mentalPurposeID {
	   		// уже был вызов при данных теме и цели
	   // TODO м.б. в таком случае привенить что-то радикальное в случае атасной ситуации...
	   		return false
	   	}
	   //}
	   	newCycklePulsCount=PulsCount
	   	oldThemeID8=problemTreeInfo.themeID
	   	oldPurposeID8=mentalInfoStruct.mentalPurposeID
	*/

	// для каждой новой цели должен начинаться свой кадр ментальной эпиз.памяти
	// !!!! clinerFuncSequence() не сбрасывать wasRunPurposeActionFunc !!!
	infoFuncSequence = nil // т.к. с каждым стимулом обновляется главный цикл resetMineCycleAndBeginAsNew()

	// если нет темы, после просыпания и т.п.
	if problemTreeInfo.themeID == 0 {
		//"Улучшение настроения" - 17 - базовая тема когда нет ничего другого
		themeID, theme := createThemeImageID(0, 2, 17, LifeTime, true)
		problemTreeInfo.themeID = themeID
		themeType := theme.Type
		mentalInfoStruct.ThemeImageType = themeType
		oldThemeID = themeID
	} else {
		// не сменилась тема
		//		themeType := ThemeImageFromID[problemTreeInfo.themeID].Type
		node, ok := ReadeThemeImageFromID(problemTreeInfo.themeID)
		if ok {
			themeType := node.Type

			if oldThemeImageType > 0 && oldThemeImageType == themeType { //  && mentalInfoStruct.mentalPurposeID >0
				// если блокировать запуск дерево проблем при одинаковой теме, то значимости от разных стимулов будут привязываться
				// к одному узлу дерева проблем, что явно не правильно. Тема может и одна, но situationTreeID и purposeID могут быть разные
				if GetMotorsAutomatizmListFromTreeId(detectedActiveLastNodID) == nil {
					// блокировать активацию при одинаковой теме только если нет вообще никакого автоматизма - чтобы сработало func13
					return false
				}
			}

			oldThemeImageType = themeType
		}
	}

	if getMentalPurpose() { // активация дерева понимания, если готовы переменные узлов дерева
		if problemTreeInfo.situationTreeID > 0 && problemTreeInfo.themeID > 0 && problemTreeInfo.purposeID > 0 {
			//if !c.idle || show_all_logs {
			c.log += "В infoFunc8() найдена Цель <b> <span style='cursor:pointer;color:blue' onClick='get_purpose(" + strconv.Itoa(mentalInfoStruct.mentalPurposeID) + ")'>" + strconv.Itoa(mentalInfoStruct.mentalPurposeID) + "</span>" + "</b><br>"
			//}

			//При вызове  infoFunc8() происходит активация дерева проблем и НЕ ДОЛЖНА БЫТЬ новая об.активация осмысления!
			if !wasRunPurposeActionFunc { // true - уже была активация ProblemTreeActivation() по func infoFunc14
				noProblemTreeActivation = true
				ProblemTreeActivation() //Активация дерева понимания проблемы
				noProblemTreeActivation = false
			}
			// если вернет true, когда надо в consciousnessElementary отработать ситуации с негативной значимостью и плохим эффектом группового правила,
			// то в usualThinkProcess не дойдет до infoFunc2, а в самой infoFunc2 не дойдет до func13
			if mentalInfoStruct.motorAtmzmID > 0 {
				return true
			}
		}
	}
	return false
}

//////////////////////////////////////
/* определить mentalInfoStruct.mentalPurposeID
 вызов из if IsFirstActivation{

Субъективная цель. Это - не сознательная цель, а мотивирующая потребность, дающая направленность мышлению.
Текущая тема должна давать контекст размышлениям вот каким образом.
Поддержание актуальной темы-контекста общения - внимание к актуальному объекту.
Для данного объекта выбирается целевая цепочка Правил,
которая начинает выполняться по шагам, всякий раз снова корректируя цепочку правил.
Так строится разговор.
*/
func getMentalPurpose() bool { // обязательно находит Цель
	//	mentalInfoStruct.mentalPurposeID=0  не нужно! т.к. обязательно находит Цель и перекрывает

	//	theme:=mentalInfoStruct.ThemeImageType //не очищать mentalInfoStruct.ThemeImageType
	//	clinerMentalInfo()// начинается новый цикл мышления
	//	mentalInfoStruct.ThemeImageType=theme

	if mentalInfoStruct.ThemeImageType == 0 || mentalInfoStruct.ThemeImageType == 17 { // не определена тема - тупо постараться улучшить настроение
		mentalInfoStruct.ThemeImageType = 17 //Улучшение настроения - базовая тема когда нет ничего другого
		id, _ := createPurposeImageID(0, 1, 3, 0, 0, true)
		mentalInfoStruct.mentalPurposeID = id
		problemTreeInfo.purposeID = id //инфа для активации дерева проблем
		return true
	}
	///////////////////////////////////
	// есть рвущийся автоматизм, или есть тема "Сомнение в штатном автоматизме"
	if mentalInfoStruct.motorAtmzmID > 0 || mentalInfoStruct.ThemeImageType == 12 {
		unode, ok := ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
		if ok {
			id, _ := createPurposeImageID(0, 2, unode.Mood,
				unode.EmotionID,
				unode.SituationID, true)
			mentalInfoStruct.mentalPurposeID = id
			problemTreeInfo.purposeID = id //инфа для активации дерева проблем
			// далее TODO НУЖНО ДУМАТЬ ПРО ЭТО
			//infoFunc6()//ПОДВЕРГНУТЬ СОМНЕНИЮ автоматизм, если нет опасности (не нужно реагировать аффектно) и ситуация важна
			return true
		}
	}
	///////////////////////////////////

	// Есть объект высокой негативной значимости
	if mentalInfoStruct.ThemeImageType == 16 {
		/* extremImportanceObject - объект наибольшой негативной значимости в воспринимаемом
		extremImportanceObject.extremVal - НАГАТИВНАЯ ЗНАЧИМОСТЬ объекта
		 определиться с Целью по этому объекту:
		 TODO	найти situacTreeID дерева ситуации в Правилах с этим объектом, где улучшается эффект
			situacTreeID:= ....
			и создать Цель:
			id,_:=createPurposeImageID(0,2,UnderstandingNodeFromID[situacTreeID].Mood,
							UnderstandingNodeFromID[situacTreeID].EmotionID,
							UnderstandingNodeFromID[situacTreeID].SituationID,true)
						mentalInfoStruct.mentalPurposeID=id
						return
		*/
	}
	///////////////////////////////////

	if mentalInfoStruct.ThemeImageType == 3 { //Состояние Плохо - достичь Хорошо т.е. после Плохи всегда бывает улучшение в Хорошо
		id, _ := createPurposeImageID(0, 1, 3, 0, 0, true)
		mentalInfoStruct.mentalPurposeID = id
		problemTreeInfo.purposeID = id //инфа для активации дерева проблем
		return true
	}

	/* Цель - улучшение данной ситуации - для Тем с ID:
	1 "Негативный эффект моторного автоматизма"
	2 "Негативный эффект ментального автоматизма"
	4 "Стимул с Пульта"
	7 "Игнорирование оператором"
	8 "Игра"
	10 "Непонимание"
	13 "Защита"
	14 "Страх"
	15 "Агрессия"
	16"Есть объект высокой значимости"
	*/

	switch mentalInfoStruct.ThemeImageType {
	case 1, 2, 4, 7, 10, 13, 14, 15, 16:
		unode, ok := ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
		if ok {
			id, _ := createPurposeImageID(0, 2, unode.Mood,
				unode.EmotionID,
				unode.SituationID, true)
			mentalInfoStruct.mentalPurposeID = id
			problemTreeInfo.purposeID = id //инфа для активации дерева проблем
			return true
		}
	}

	/* Цель - повторнеие (достижение) данной ситуации - для Тем с ID:
	1 "Негативный эффект моторного автоматизма"
	2 "Негативный эффект ментального автоматизма"
	3 "Состояние Плохо"
	4 "Стимул с Пульта"
	5 "Поисковый интерес"
	6 "Обучение с учителем"
	8 "Игра"
	9 "Неудовлетворенность существущим"
	*/
	switch mentalInfoStruct.ThemeImageType {
	case 5, 6, 8, 9:
		unode, ok := ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
		if ok {
			id, _ := createPurposeImageID(0, 1, unode.Mood,
				unode.EmotionID,
				unode.SituationID, true)
			mentalInfoStruct.mentalPurposeID = id
			problemTreeInfo.purposeID = id //инфа для активации дерева проблем
			return true
		}
	}
	///////////////////////////////////

	if mentalInfoStruct.ThemeImageType == 11 { //Действие оператора - тут TODO лучше выбрать объект по Правилам!
		/* есть доступная инфа о действии оператора:
		   	curStimulImage=curActiveActions
		      	curStimulImageID=curActiveActionsID
		       curActiveVerbalID = verbID
		      	curActions.PhraseID = PhraseID
		      	curActions.ToneID = ToneID
		      	curActions.MoodID = MoodID

		   		TODO лучше выбрать объект по Правилам!
		*/
		unode, ok := ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
		if ok {
			id, _ := createPurposeImageID(0, 2, unode.Mood,
				unode.EmotionID,
				unode.SituationID, true)
			mentalInfoStruct.mentalPurposeID = id
			problemTreeInfo.purposeID = id //инфа для активации дерева проблем
			return true
		}
	}

	////////////////////// Цели - поиск в Правилах TODO !!!!!
	/*
		// Цели - поиск в Правилах

		// Текущая тема должна давать контекст размышлениям вот каким образом.
		Поддержание актуальной темы-контекста общения - внимание к актуальному объекту.
		Для данного объекта выбирается целевая цепочка Правил, которая начинает выполняться по шагам,
		всякий раз снова корректируя цепочку правил. Так строится разговор.*/
	/*

			//ищем PurposeImage.actionID в контексте активных деревьев

			найти - с учетом Правил!!!!
			??На стадии 4 - провоцировать оператора на ответы (почему, зачем, что такое?)
		Нужно искать не только в контексте эмоции, а активных веток деревьев detectedActiveLastNodID и detectedActiveLastUnderstandingNodID
			Алгоритм:
		Ищем в ОБЪЕКТИВНЫХ Правилах подходящее действие ,
			смотрим в последних кадрах эпизод.памяти такое Правило и в его продолжении есть позитивный эффект,
			то берем оттуда действие оператора.*/
	/*
	   	if EpisodeMemoryObjects!=nil {// еще нет эпиз.памяти, так что и цели нет...
	   		return
	   	}
	   // тупо - для 4-й ступени (и 5-й, если нет Доминанты), когда набирается значимость и Правила
	   		indexE := 0 // индекс в массиве эпизод.памяти, чтобы по нему смотреть, что было дальне
	   		rID, exact := getRulesArrFromTrigger(curActiveActionsID,true)
	   		if exact > 3 { // слишком сомнительно

	   			rulexID := 0
	   			maxSteps := 1000
	   			for limit := 5; limit > 1; limit-- {
	   				rulexID, indexE = getRulesFromEpisodicsSlice(limit, maxSteps)
	   				if rulexID > 0 {
	   					break
	   				}
	   				maxSteps = maxSteps / 2
	   			}
	   		} else { // вполне уверенно найдено Правило, ищем его индекс в эпиз.памчяти
	   			if EpisodeMemoryObjects!=nil {
	   				for i := len(EpisodeMemoryObjects); i >= 0; i-- {
	   					ep := EpisodeMemoryObjects[i]
	   					if ep.Type == 0 && ep.TriggerAndActionID == rID {
	   						indexE = i
	   						break
	   					}
	   				}
	   			}
	   		}
	   		//////////////////////////
	   		if indexE == 0 {
	   			return
	   		}

	   		// есть ли последующий кадр эпизод.памяти
	   		lastEM := EpisodeMemoryObjects[indexE+1]
	   		if lastEM == nil {
	   			return  // придется обойтись без PurposeImage.actionID
	   		}
	   		// хороший ли эфект
	   		rArr := rulesArr[lastEM.TriggerAndActionID]
	   		if rArr == nil {
	   			return  // придется обойтись без PurposeImage.actionID
	   		}
	   		// выдать конечное праило, если оно с хорошим эффектом
	   		var rеserve = 0 // резервные Правила, если не найдено точно в контексте
	   		ta := TriggerAndActionArr[lastEM.TriggerAndActionID]
	   		if ta == nil {
	   			return // придется обойтись без PurposeImage.actionID
	   		}
	   		if ta.Effect > 0 { // с хорошим эффектом
	   			rеserve = ta.Action
	   			if lastEM.NodeAID == detectedActiveLastNodID {
	   				rеserve = ta.Action
	   				if lastEM.NodeSID == detectedActiveLastUnderstandingNodID {
	   					rеserve = ta.Action
	   				}
	   			}
	   		}
	   		if rеserve > 0 { // нашли действие
	   			if savePurposeGenetic == nil {
	   				getPurposeGenetic()
	   			}
	   			// создать Цель
	   // !!!! purposeID, _ := createPurposeImageID(0, 2,savePurposeGenetic.veryActual, savePurposeGenetic.targetID, rеserve, true)
	   			// передать результат
	   			mentalInfoStruct.mentalPurposeID = 0 //purposeID
	   	}
	      ////////////////////// Цели - поиск в Правилах TODO !!!!!
	*/

	/*   оставляю как пример расшифровки эмоции
	// определить эмоциональный контекст по newEmotionID или прямо по CurrentEmotionReception
	contextArr:=CurrentEmotionReception.BaseIDarr
	for i := 0; i < len(contextArr); i++ {
		switch contextArr[i]{
		case 1:	//Пищевой	- Пищевое поведение, восполнение энергии, на что тратится время и тормозятся антагонистические стили поведения.

		case 2:	//Поиск	- Поисковое поведение, любопытство. Обследование объекта внимания, поиск новых возможностей.

		case 3:	//Игра	- Игровое поведение - отработка опыта в облегченных ситуациях или при обучении.

		case 4:	//Гон	- Половое поведение. Тормозятся антагонистические стили

		case 5:	//Защита	- Оборонительные поведение для явных признаков угрозы или плохом состоянии.

		case 6:	//Лень	- Апатия в благополучном или безысходном состоянии.

		case 7:	//Ступор	- Оцепенелость при непреодолимой опастbase_context_activnostности или когда нет мотивации при благополучии или отсуствии любых возможностей для активного поведения.

		case 8:	//Страх	- Осторожность при признаках опасной ситуации.

		case 9:	//Агрессия	- Агрессивное поведение для признаков легкой добычи или защиты (иногда - при плохом состоянии).

		case 10: //Злость	- Безжалостность в случае низкой оценки .

		case 11: //Доброта	- Альтруистическое поведение.

		case 12: //Сон - Состояние сна. Освобождение стрессового состояния. Реконструкция необработанной информации.

		}
	}
	*/

	// ? TODO мозжечковые рефлексы - самый первый уровень осознания - подгонка действий под заданную Цель?

	/* ДОМИНАНТА - осознаваемая цель - решение доминирующей осознанно сформированной проблемы
	Может по весу значимости перекрывать текущую Цель mentalInfoStruct.mentalPurposeID
	*/
	if EvolushnStage > 4 { // главное - Доминанта нерешенной пробелмы
		//dominfntaID := getMainDominanta(CurrentEmotionReception.ID)
		//if dominfntaID > 0 {
		getCurrentDominant()
		if CurrentProblemDominanta != nil {
			// TODO: использовать Цель Доминанты и определить по ней mentalInfoStruct.mentalPurposeID= !!!!
			//TODO:  ?sкакой должна быть цель Доминанты, чтобы по ней можно было определить mentalInfoStruct.mentalPurposeID

			return true
		}
	}

	// ?????????????????????????????
	//	createAndRunPurposeAutomatizm()

	return false
}

// //////////////////////////////////
func createAndRunPurposeAutomatizm() {
	if mentalInfoStruct.mentalPurposeID == 0 {
		return
	}
	// создать мент.автоматизм приоизвольной активацции ментальной цели
	/*	actImgID,_:=CreateNewlastMentalActionsImagesID(0,3,mentalInfoStruct.mentalPurposeID,true)

		id, matmzm := createMentalAutomatizmID(0, actImgID, 1)
		if id >0 {
			// запустить мент.автоматизм
			RunMentalAutomatizm(matmzm)

		}*/
}

//////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/mental_memories.go
____________________________________________
/* Короткие цепочки вспомогательной ментальной памяти.
Два вида короткой памяти:
1. Стек прерывания размышления
2. Стек обобщения смыслов воспринятого
Величина этих стеков должна оптимизироваться к текущим условиям жизни.
Здесь же она задается жестко - по 10 ячеек памяти в каждом стеке.
______________________________________
и стек памяти прерванных размышлений  var InterruptMemory []*InterruptImage
где созраняется прерванный ID итерации цикла, ID темы и ID поставленной цели,
Так что при возврате к прерванному размышлению, восстанавлявается его образы.
Этот стек сохраняется в файле чтобы после перезагрузки можно было начать с прерванного.
_____________________________________
Стек памяти обобщения смыслов - буфер для сбора отдельных смыслов
при восприятии флинной фразы, которую нужно разбить на отдельные смыслы (importance)
Этот стек НЕ сохраняется в файле.
При восприятии фразы с пульта нужно смотреть, распознается ли смысл (importance) по мере прохождения, как только
удается понять текущий смысл фрагмента, если да, то фрагмент кладется в стек обобщения и туда добавляются все смыслы,
чтобы затем понять общий. Ребенок узнает слово по буквам, вставляя в стек отдельные буквы.

Пока что остается за скобками то, что использует стек обобщения:
ТВОРЧЕСКОЕ СОПОСТАВЛЕНИЕ И ОБОБЩЕНИЕ
ТВОРЧЕСКАЯ ФАНТАЗИЯ
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/*
	стек для обобщений: 7 Базовых cycleID

Сохраняет 7 Базовых cycleID (звенья начала цепочки)
*/
var saveBaseLinksCicleSynthesis []*int

func addMewBaseLinksMemory(BaseLinksID int) {
	if len(saveBaseLinksCicleSynthesis) > 7 {
		// удалить первый
		saveBaseLinksCicleSynthesis = saveBaseLinksCicleSynthesis[1:]
	}

	saveBaseLinksCicleSynthesis = append(saveBaseLinksCicleSynthesis, &BaseLinksID)
}

//////////////////////////////////////////////////////////////////////////

/////////////////////   стек памяти прерванных размышлений
/* стек до 7 прерванных задач
Запоминаются в файле и актуализируются при просыпании

Здесь нет базовых условий, и поэтому при их изменении образ прерванной цели обессмысливается,
чтобы вспомнить нужно опять попасть в ситуацию с прерыванием.
Можно улучшить природу и сделать полное восстановление условий ментальным перезапуском,
но это может привести к неожиданным последствиям.
*/
// образ прерванного размышления
type InterruptImage struct {
	cycleID        int // на каком ID goNext было прерванно размышление
	ThemeImageType int //Type темы размышления
	PurposeImageID int //ID поставленной цели
	ExtremObjID    int // extremImportanceObject.extremObjID
}

var InterruptMemory []*InterruptImage

// добавить в стек прерывание
func addInterruptMemory(cycleID int) {
	// Добавить в стек до 7 прерванных решений
	if len(InterruptMemory) > 7 {
		// удалить первый
		InterruptMemory = InterruptMemory[1:]
	}
	var node InterruptImage
	node.cycleID = cycleID
	node.ThemeImageType = mentalInfoStruct.ThemeImageType
	node.PurposeImageID = mentalInfoStruct.mentalPurposeID
	if extremImportanceObject != nil {
		node.ExtremObjID = extremImportanceObject.objID
	}
	InterruptMemory = append(InterruptMemory, &node)
}

////////////////////////////////////////////////////////////////

func loadInterruptMemory() {
	InterruptMemory = nil
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/interrupt_memory.txt")
	cunt := len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		var node InterruptImage
		node.cycleID, _ = strconv.Atoi(p[0])
		node.ThemeImageType, _ = strconv.Atoi(p[1])
		node.PurposeImageID, _ = strconv.Atoi(p[2])
		InterruptMemory = append(InterruptMemory, &node)
	}
}
func saveInterruptMemory() {
	var out = ""
	for n := 0; n < len(InterruptMemory); n++ {
		out += strconv.Itoa(InterruptMemory[n].cycleID) + "|"
		out += strconv.Itoa(InterruptMemory[n].ThemeImageType) + "|"
		out += strconv.Itoa(InterruptMemory[n].PurposeImageID)
	}
	out += "\r\n"
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/interrupt_memory.txt", out)
}

//////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////
/////////////////////////  стек обобщения смыслов
/*  Здесь хранятся распознанные  ID смыслов типа type importance struct
для элементов фразы Стимула из образа действия ActionsImage или нескольких подрях ActionsImage
т.е. смысл обобщается не только в одной длинной фразе, а и в ходе диалога.

*/
// образ прерванного размышления
type SynthesizeImportanceImage struct {
	PhraseID     []int // // массив фразID (DetectedUnicumPhraseID) слова каждой фразы вытаскиваются wordSensor.GetWordArrFromPhraseID(PhraseID[0])
	importanceID int   //ID importance (var importanceFromID=make(map[int]*importance))
}

var SynthesizeImportanceMemory []*SynthesizeImportanceImage

/* Тут должны быть функци работы с обобщением нескольких смыслов в один общий.
Это - пока не очень понятный алгоритм
НО В ДАННОЙ РЕАЛИЗАЦИИ Beast нет особого смысл в обобщении смыслов...
*/
// TODO когда-нибудь, но это  не принципиально для общей системы

///////////////////////////////////////////////////////////////////////

/*
	Был запущен штатный автоматизм после Стимула. Выясняется на 1-м и 2-м уровне осмысления.

Если был запущен автоматизм, то блокировать запуски последующих, кроме ментального запуска wasRunPurposeActionFunc.
Если вдруг нужно будет, подумав, запустить ментально автоматизм, то нужно wasRunTreeStandardAutomatizm=false.
wasRunTreeStandardAutomatizm=true устанавливается при любом запуске автоматизма в func RumAutomatizm

var wasRunTreeStandardAutomatizm = false  ТЕПЕРЬ всегда после запуска автоматизма LastRunAutomatizmPulsCount >0
*/

/*
последовательность ID выполненных инфо-функций, ментальный эизод памяти, в одной активации consciousnessElementary()
infoFuncSequence=append(infoFuncSequence,infofID) - в
для ментальных правил func afterWaitingPeriod(effect int) в understanding_process.go
Когда сработал автоматизм и после периода ожидания возник Стимул,
то происходит запись м.Правил func afterWaitingPeriod(effect int) в understanding_process.go
с детектором достижения Цели getMentalEffect(effect0 int)
После этого или после завершения периода ожидания (clinerAutomatizmRunning()) infoFuncSequence очищается.

TODO м.б. не самая последняя функция, а ряд последних дает эффект мент.Правила!...
*/
var infoFuncSequence []int

var wasRunPurposeActionFunc = false //true - сработала func infoFunc17, 14 и 26 - прекратилась запись в infoFuncSequence[]
func clinerFuncSequence() {
	infoFuncSequence = nil
	wasRunPurposeActionFunc = false
	//curFunc13ID = 0
}

var wasRunProvocationFunc = false //true - сработала провокационная функция infoFunc31

//////////////////////////////////////////////////////////////////////////

func initMentalMemories() {
	//savePorposeIDcurrentCicle=nil
	saveBaseLinksCicleSynthesis = nil
	loadInterruptMemory()
}

//////////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/mood.go
____________________________________________
/* Настроение: сила Плохо -10 ... 0 ...+10 Хорошо.
Ощущение силы Плохо и сил Хорошо и это информационно для оценок последствий действий.
Настроение в зависимости от гомеостатический параметров
а так же оченка опасноного состояния
*/


package psychic

import (
	actionSensor "BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	"BOT/lib"
)

////////////////////////////////////////
/* CurrentMood - ТЕКУЩАЯ ОЦЕНКА ИЗМЕНЕНИЯ гомео-СОСТОЯНИЯ (гомео-настроения) - сила ощущаемого настроения PsyBaseMood
-10 0 +10 изменение настроения
- основа для стремления и избегания.
*/
var CurrentMood=0 // Так же текущее значение - в CurrentInformationEnvironment.Mood
// предыдуще значение. Так же предыдущее значение - в OldInformationEnvironment.Mood
var PreviousMood=0

/* Определячет первый уровень образа Цели.
Субъективно ощущаемая оценка, текущее осознаваемое настроение, которое можно произвольно изменять.
Она стремится к нулю со временем.
Постоянное состояние Хорошо довольно быстро уходит, постоянное состояние Плохо уходит гораздо медленнее.
При резких изменениях возникает эффект "маятника настроения":
появление противоположного по знаку настроения, но меньшего значения.
Значение обновляется при значительных изменениях (CurrentMood - PreviousMood)
Предыдущее значение - в OldInformationEnvironment.PsyMood
 */
var PsyMood=0 // плохо -10...0...10 хорошо - сила ощущаемого настроения PsyBaseMood  корректируется с каждым пульсом moodePulse()
var PsyMoodPulse=0 // последнее изменение PsyMood в пульсах
var veryMachChanged=false // true - было сильное изменение настроения
/////////////////////////////////////////////////////////////////////////////////////
var PsyBaseMood=0 // -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение

/////////////////////////   БОЛЬ И РАДОСТЬ    //////////////////////////////
/* Основа этих величин - значение, получаемые в http://go/pages/gomeostaz.php
в таблицах "Активности Базовых стилей" и "Действия оператора - гомеостатический эффект".
Боль и Радость на доосознательном уровне НЕ имеют рефлекторное назначение
и используются ТОЛЬКО для психики в определении последствий реакции.

В каких животных появляется ощущение боли в ходе эволюционных усложнений?
Учитывая, что ощущение боли не возникает на уровне рефлексов, это психический феномен.
Поэтому и возникает вопрос: чтобы боль могла ощущаться, нужны какие-то рецепторы,
которые возникли задолго до появления психики.
В каких животных есть такие рецепторы, но еще нет ощущения боли?

Вопрос является сложным и до конца неисследованным.
Однако, исследования показывают, что рецепторы боли (нервные окончания,
способные реагировать на повреждения) могут существовать у разных видов животных,
даже если у них нет развитой психики и способности к осознанию боли.
Это объясняется тем, что ощущение боли является эволюционно выгодным механизмом защиты
от потенциальных угроз и повреждений.
Рецепторы боли могут помочь животному избегать опасных ситуаций и предотвращать повторные повреждения.
Некоторые исследования показывают, что рецепторы боли найдены у различных групп животных,
таких как беспозвоночные (например, раки и сепии), рыбы и даже некоторые примитивные бесхвостые хордовые
(например, амфиоксусы). Однако, понимание того,
как именно эти животные воспринимают или осознают болевые сигналы, остается предметом дальнейших исследований.

Так что на допсихическом уровне детекция Боли и Радости осуществляется в gomeostas.go в виде:
var GomeostazActionEffectPainV=0 // величина Боли
var GomeostazActionEffectJoyV=0 // величина радости
Но только безусловные рефлексы (и условные, созданные на их основе) могут использовать эти детекторы.
Однако, у же на том уровне есть детектор СтелоЛучшеИлиХуже (func BetterOrWorseNow()),
который используется в психике для оценки эффекта ответного реагирования.
На уровне рефлекснв нет Боли, а есть только детекторы травматических последствий.
Боль и радость: fornit.ru/67646
TODO Их мы могли бы использовать в безусловных рефлексов и это стоит сделать.
 */
var painValue=0 // величина Боли
var joyValue=0 // величина радости

///////////////////////////////////////////////////////////////////////////////////////

/* ПУЛЬС психики
var PulsCount=0 // передача тика Пульса из brine.go
var LifeTime=0
var EvolushnStage=0 // стадия развития
var IsSleeping=false
 */
func moodePulse(){
	updatePsyMood(false)
}

/* Обновляется при инициализации GetCurrentInformationEnvironment() до и после совершения действий
Это безусловно-рефлекторная оценка на освнове изменений
1) гомео параметров (улавливает их изменение >4%) и 2) основы базовых контекстов: 0- НОРМА, 1-ПЛОХО, 2-ХОРОШО
Хотя базовые контексты уже создаются на основе жизненных параметров,
но это - безусловный рефлекс на основе безусловного рефлекса активации базовых контекстов.
На этой основе происходит конкуренция Целей при их выборе
и, как следствие, произвольная оценка эмоционального состояния.
*/
func GetCurMood()(int){
	danger:=GetAttentionDanger()
	var mood =0
	GomeostazParams:=gomeostas.GomeostazParams
	OldGomeostazParams:=gomeostas.OldGomeostazParams
	CommonBadNormalWell:=gomeostas.CommonBadNormalWell
	OldCommonOldBadValue:=gomeostas.OldCommonBadNormalWellVal

	for n := 1; n < 9; n++ {
		if OldGomeostazParams[n]!=0{
			// процент изменения параметра от предыдущего значения позитивный или негативный:
			var proc=100*(GomeostazParams[n]-OldGomeostazParams[n])/OldGomeostazParams[n]

			switch n {
			case 1: // энергия   +/- 10
				if proc > 4{
					if danger{ mood+=6} else{mood+=3}
				}
				if proc < -4{
					if danger{ mood-=6} else{mood-=3}
				}
			case 2: // стресс  +/- 20
				if proc > 6{
					if danger{ mood-=4} else{mood-=2}
				}
				if proc < -6{
					if danger{ mood+=4} else{mood+=2}
				}
			case 3: // гон
				if proc > 20{
					if danger{ mood-=1} else{mood-=1}
				}
				if proc < -20{
					if danger{ mood+=1} else{mood+=1}
				}
			case 4: // потребность в общении +/- 20
				if proc > 10{
					if danger{ mood-=1} else{mood-=1}
				}
				if proc < -10{
					if danger{ mood+=1} else{mood+=1}
				}
			case 5: // потребность в обучении +/- 20
				if proc > 10{
					if danger{ mood-=1} else{mood-=1}
				}
				if proc < -10{
					if danger{ mood+=1} else{mood+=1}
				}
			case 6: // 	Поиск любопытство  +/- 20
				if proc > 10{
					if danger{ mood+=1} else{mood+=1}
				}
				if proc < -10{
					if danger{ mood-=1} else{mood-=1}
				}
			case 7: // Самосохранение (Жадность, эгоизм, самозащита, страх смерти. Зависит от ситуации, может сам уменьшаться при благополучии.)
				if proc > 20{
					if danger{ mood-=1} else{mood-=1}
				}
				if proc < -20{
					if danger{ mood+=1} else{mood+=1}
				}
			case 8: // Повреждения
				if proc > 10{
					if danger{ mood-=1}
				}else {
					if proc > 20 {
						if danger {
							mood -= 5
						}
					}
				}
				if proc > 40 {
				// при proc==40 mood=-6 а при proc==100 mood=-10
					mood=-int(10.0 - (100.0-proc)/15.0)
				}
			}//switch n
		}
		OldGomeostazParams[n]=GomeostazParams[n]
	}

	switch CommonBadNormalWell {
	case 2: // НОРМА
		if OldCommonOldBadValue==1 { // было ПЛОХО
			if danger{ mood+=5} else{mood+=2}
		}
		if OldCommonOldBadValue==3 { // было ХОРОШО
			if danger{ mood-=5} else{mood-=2}
		}
	case 1: // ПЛОХО
		if OldCommonOldBadValue==3 { // было ХОРОШО
			if danger{ mood-=5} else{mood-=2}
		}
		if OldCommonOldBadValue==2 { // было НОРМА
			if danger{ mood-=3} else{mood-=1}
		}
	case 3: // ХОРОШО
		if OldCommonOldBadValue==2 { // было НОРМА
			if danger{ mood+=3} else{mood+=1}
		}
		if OldCommonOldBadValue==1 { // было ПЛОХО
			if danger{ mood+=6} else{mood+=3}
		}
	}

	// при значениях Боли или Радости >5 они определяют настроение
	moorReduce:=mood/3
	painValue,joyValue=gomeostas.GetCurPainJoy()
	if painValue>5{
		mood=moorReduce-painValue
	}
	if joyValue>5{
		mood=moorReduce+joyValue
	}

	if mood > 10{mood=10}
	if mood < -10{mood=-10}

	PreviousMood=CurrentMood
	CurrentMood=mood

	updatePsyMood(true)
	updatePsyBaseMood()

	return mood
}
///////////////////////////////////////////////////////////////////



/* оценить опасность текущей ситуации: да-нет
 */
func GetAttentionDanger()(bool){
	// опасные жизненные параметры
	for k, pID := range gomeostas.BadNormalWell {
		if pID==1 { // плохо для данного параметра гомеостаза
			if k == 1 || k == 2 || k == 7 || k == 8 {
				return true
			}
		}
	}
	// по опасному действию с Пульта

	aArr:=actionSensor.CheckCurActionsContext()
	for i := 0; i < len(aArr); i++ {
		if aArr[i]==3 || aArr[i]==10 || aArr[i]==12 || aArr[i]==15{
			return true
		}
	}
	/*
	// по опасной фразе с Пульта
	if isDangerWordFromPult(){
		return true
	}
	*/
	return false
}

/* PsyMood стремится к нулю со временем.
Постоянное состояние Хорошо довольно быстро уходит, постоянное состояние Плохо уходит гораздо медленнее.
При резких изменениях возникает эффект "маятника настроения":
появление противоположного по знаку настроения, но меньшего значения.
 */
func updatePsyMood(newMood bool){
	if PreviousMood==0{// первое значение - просто копируем
		PsyMood=CurrentMood
		PsyMoodPulse=PulsCount
		return
	}
	// было ли очень сильное изменение - для маятника настроения
	if (lib.Abs(PsyMood - CurrentMood) > 6) || (lib.Abs(CurrentMood)>=3 && lib.IsDiffersOfSign(PsyMood,CurrentMood)) {
		veryMachChanged=true
	}
	// PsyMood изменяется при значительных измнениях CurrentMood
	if lib.Abs(PsyMood - CurrentMood) > 3{
		PsyMood=CurrentMood
		PsyMoodPulse=PulsCount
		return
	}
	// PsyMood изменяется при смене знака значения (одно положительное, другое отрицательное),
	// большего, чем 1 (чтобы не было постоянной смены около нуля)
	if lib.Abs(CurrentMood)>=1 && lib.IsDiffersOfSign(PsyMood,CurrentMood){
		PsyMood=CurrentMood
		PsyMoodPulse=PulsCount
		return
	}
	// маятник настроения - через 30 пульсов
	if veryMachChanged && (PulsCount-PsyMoodPulse>30){
		PsyMood=-(PsyMood-3)// смена настроение на противоположное, но уменьшенное значение
		PsyMoodPulse=PulsCount
		veryMachChanged=false
		return
	}
	////////// режим постепенного угасания
	if PsyMood>0 { // хорошее настроение угасает довольно быстро
		if PulsCount-PsyMoodPulse > 30 {
			if lib.Abs(CurrentMood) > 0 {
				if PsyMood > 0 {
					PsyMood--
				} else {
					PsyMood++
				}
				PsyMood = CurrentMood
				PsyMoodPulse = PulsCount
			}
		}
	}
	if PsyMood<0 { // плохое настроение более важно, угасает медленнее
		if PulsCount-PsyMoodPulse > 60 {
			if lib.Abs(CurrentMood) > 0 {
				if PsyMood > 0 {
					PsyMood--
				} else {
					PsyMood++
				}
				PsyMood = CurrentMood
				PsyMoodPulse = PulsCount
			}
		}
	}
}

/* ощущаемое настроение (-1,0,1)
обновляется сразу после updatePsyMood
 */
func updatePsyBaseMood(){
	if PsyMood<=1{
		PsyBaseMood=-1
	}
	if PsyMood<2 && PsyMood>-2{
		PsyBaseMood=0
	}
	if PsyMood>=1{
		PsyBaseMood=1
	}
}
____________________________________________

BOT/brain/psychic/orientation_reflex_functions.go
____________________________________________
/*  Вспомогательные функции Ориентировочных рефлексоы

 */

package psychic

/////////////////////////////////////////////////////////

//////////////////////////////////////////////
/* ТОЛЬКО ДЛЯ orientation_1(), когда автоматизма нет у недоделанной ветки!
сформировать пробный автоматизм моторного действия и сразу запустить его в действие
   Зафиксироваь время действия
   25 пульсов следить за измнением жизненных параметров и ответными действиями - считать следствием действия
   оценить результат и скорректировать силу мозжечком в записи автоматизма.
*/
func createAutomatizm(pc *PurposeGenetic) *Automatizm {
	if pc == nil || pc.actionID == nil {
		return nil
	}

	BranchID := detectedActiveLastNodID

	aArr := pc.actionID.ActID

	sArr := pc.actionID.PhraseID

	// тон и настроение
	t := pc.actionID.ToneID
	/* настроение при передаче фразы с Пульта:
		20-Хорошее    21-Плохое    22-Игровое    23-Учитель    24-Агрессивное   25-Защитное    26-Протест
	ID возникает при добавлении 19 к номеру радиокнопки пульта, например, для Хорошее 1+19=20
	*/
	m := pc.actionID.MoodID - 19
	if m < 0 {
		m = 0
	} //есть еще Нормальное настроение==0

	ActionsImageID, _ := CreateNewlastActionsImageID(0, 0, aArr, sArr, t, m, true)
	// создать автоматизм
	_, atzm := CreateNewAutomatizm(BranchID, ActionsImageID)
	if atzm != nil {
		atzm.Energy = 5

		return atzm
	}

	return nil
}

//////////////////////////////////////////////

/////////////////////////////////////
/*подобрать по тону и настроению хоть как-то ассоциирующуюся фразу из имеющихся
Tone int //Тон: 0 - обычный, 1 - восклицательный, 2- вопросительный, 3- вялый, 4 - Повышенный
Mood int // настроение при передаче фразы с Пульта: 20-Хорошее    21-Плохое    22-Игровое    23-Учитель    24-Агрессивное   25-Защитное    26-Протест
*/
func findSuitablePhrase() []int {
	var ToneID = 0
	var MoodID = 0
	if PsyBaseMood == -1 { // плохое настроение
		MoodID = 21
		ToneID = 4
		if CurrentInformationEnvironment.danger { // опасность состояния
			ToneID = 1
			MoodID = 25 // защитное
		}
	}
	if PsyBaseMood == 0 { // нормальное настроение
		MoodID = 0
		ToneID = 0
		if CurrentInformationEnvironment.danger { // опасность состояния
			ToneID = 4
			MoodID = 24 // защитное
		}
	}
	if PsyBaseMood == 1 { // хорошее настроение
		MoodID = 20
		ToneID = 4
	}
	for _, v := range VerbalFromIdArr {
		if v == nil {
			continue
		}
		if v.ToneID == ToneID && v.MoodID == MoodID {
			return v.PhraseID
		}
	}

	return nil
}

///////////////////////////////////////////////

////////////////////////////////////////////////
/* найти важные (по опыту) признаки в новизне NoveltySituation
Это - чисто рефлексторный процесс поиска в опыте
*/
func getImportantSigns() bool {
	lenN := len(CurrentAutomatizTreeEnd)
	if lenN == 0 {
		return false
	}
	var news []int // выделить новизну
	for i := 0; i < lenN; i++ {
		if CurrentAutomatizTreeEnd[i] > 0 {
			news = append(news, CurrentAutomatizTreeEnd[i])
		}
	}
	if news == nil {
		return false
	}
	// выделить признаки и оценить важность
	switch currentStepCount {
	case 3: //остается после lev3 - ActivityID образ сочетания пусковых стимулов (только кнопок!)
		/* пример CurrentAutomatizTreeEnd:
		   lev4 90   GetToneMoodID(verb.ToneID, verb.MoodID)
		   lev5 17   verb.SimbolID
		   lev6 132  verb.PhraseID[0]
		*/
		ton, mood := GetToneMoodFromImg(90)
		// 1-восклицательный,4 - Повышенный, настроение: 2-Плохое, 5-Агрессивное, 6-Защитное
		if ton == 1 || ton == 4 || mood == 2 || mood == 5 || mood == 6 {
			return true
		}
		if EvolushnStage > 3 {
			// TODO опредять по словарному запасу в контексте дерева понимания
		}
	case 4: //SimbolID
		/* пример CurrentAutomatizTreeEnd:
		   lev5 17   verb.SimbolID
		   lev6 132  verb.PhraseID[0]
		*/
		if EvolushnStage > 3 {
			// TODO опредять по словарному запасу в контексте дерева понимания
		}

	case 5: // ToneMoodID
		/* пример CurrentAutomatizTreeEnd:
		   lev6 132  verb.PhraseID[0]
		*/
		if EvolushnStage > 3 {
			// TODO опредять по словарному запасу в контексте дерева понимания
		}

	default:
		return false
	}

	return false
}

____________________________________________

BOT/brain/psychic/orientation_reflexes.go
____________________________________________
/*  Ориентировочные рефлексы
Привлечение осознанного внимания выявляет конечную цель - найти автоматизм или ничего не делать.

Првильно было бы сделать конкуренцию стипулов с Пульта, чтобы в дереве автоматизмов удерживался только самый актуальный.
При появлении стимулов с пульта нужно проверять, что новый стимул оказывается более актуальным, чем текущий
и тогда он активирует дерево автоматизмов, иначе остается прежняя активация с размышлениями.
Это эмуляция выбора наиболее актуального ориентировочным рефлексом.
И тут потом может вмешиваться актуальность со стороны психики при мышлении,
т.е. оно может задуматься и не замечать стимулов кроме самых сильных.
Это привет к болешьей стабильности условий, не будет постоянных скачков как при недержании внимания СДВГ.
НО в данной реализации нет потока параллельно идущих стимулов
и поэтому нет насущной необходимости вводить конкурентный отбор стимулов.
Все стимулы с Пульта активируют дерево автоматизмов.

Но некоторые особенности сопровождающих это процессов тут реализованы,
срабатывающие сразу после активации дерева автоматизмов.

Начинается с Определение Цели в данной ситуации - на уровне наследственных функций
и активных доминант нерешенной проблемы.

Новизна при активации Дерева автоматизмов CurrentAutomatizTreeEnd будет сохранена при выполнении автоматизма
*/

package psychic

import (
	"BOT/lib"
)

var NoveltySituation []int // НОВИЗНА СИТУАЦИИ сохраняет значение CurrentAutomatizTreeEnd[] для решений

/*
	получить инфу после активации дерева рефлексов

Активация Дерева рефлексов всегда оказывается раньше, чем активации дерева понимания
и здесь получаем информацию о результате.
*/
var actualRelextActon []int
var veryActualSituation = false
var curTargetArrID []int
var flgConditionReflexes bool // принимаем из пакета reflexes флаг наличия безусловных рефлексов
func GetReflexInformation(veryActual bool, targetArrID []int, acrArr []int, flgConditionReflexesIdArr bool) {
	//! получить при активации древа!!!! veryActualSituation=veryActual
	actualRelextActon = acrArr
	flgConditionReflexes = flgConditionReflexesIdArr
	//! получить при активации древа!!!!curTargetArrID=targetArrID
}

// сбросить акции рефлексов
func DeleteActualRelextActon() {
	actualRelextActon = nil
	flgConditionReflexes = false
}

// пульс PulsCount
func orientarionPuls() {

	/*  если еще не запущен автоматизм  НЕ НУЖНО ВЫЗЫВАТЬ ВСЕ ВРЕМЯ!!!
	if LastRunAutomatizmPulsCount==0{//20 сек ожидания (if LastRunAutomatizmPulsCount+20 < PulsCount {)
		orientation(saveAutomatizmID)
		saveAutomatizmID=0
	}
	*/
}

/*  Выполнение ориентировочного рефлекса из активной ветки Дерева автоматизмов.
automatizmID: 0 - в активной ветке нет автоматзма, >0 - есть автоматизм
*/
//var saveAutomatizmID=0

// вызывается из func afterTreeActivation()
func orientation(automatizmID int) int {

	lib.WritePultConsol("Ориентировчный рефлекс Дерева моторных автоматизмов.")

	notAllowScanInTreeThisTime = true
	//	saveAutomatizmID=automatizmID
	var atmtzm *Automatizm
	if automatizmID == 0 {
		//автоматизма нет, если нужно действовать, то какой-то предположить и сразу проверить
		atmtzm = orientation_1()
	}
	if automatizmID > 0 {
		//проверить подходит ли автоматизм defAutomatizmID к текущим условиям
		atmtzm = orientation_2(automatizmID)
	}
	if atmtzm != nil {
		if atmtzm.BranchID == 0 {
			atmtzm.BranchID = detectedActiveLastNodID
		}
		notAllowScanInTreeThisTime = false
		return atmtzm.ID
	}
	notAllowScanInTreeThisTime = false
	return 0
}

/*
	автоматизма нет, если нужно действовать, то какой-то предположить и сразу проверить

Стадия отсуствия опыта в данных условиях.
*/
func orientation_1() *Automatizm {

	lib.WritePultConsol("Простейший ориентировочный рефлекс полного непонимания (1 типа)")

	// новизна ситуации
	NoveltySituation = CurrentAutomatizTreeEnd // значение сохраняется в savedNoveltySituation

	//  получение текущего состояния информационной среды: отражение Базового состояния и Активных Базовых контекстов
	GetCurrentInformationEnvironment()

	// оценка опасности ситуации, необходиомсть срочных действий
	veryActualSituation = CurrentInformationEnvironment.veryActualSituation
	// выявить ID парамктров гомеостаза как цели для улучшения в данных условиях
	curTargetArrID = CurrentInformationEnvironment.curTargetArrID

	if EvolushnStage < 3 { // до формирования зеркальных !!!!!
		/* Определение Цели в данной ситуации - ну уровне наследственных функций
		Здесь выбирается действие пробного автоматизма из выполнившегося рефлекса actualRelextActon
		и запускается автоматизм
		*/
		atmzm := getPurposeGeneticAndRunAutomatizm() // в purpose_genetic.go
		return atmzm
	}

	if EvolushnStage == 3 { // формирование зеркальных
		/* если нет автоматизма на стимул - просто повторить его как попугай.
		Оператор на нее ответит - и сработает функция отзеркаливания,
		сформируется автоматизм, как отвечать на фразу.
		Думается, это вполне могло оказаться эволюционной находкой:
		не знаешь как реагировать, повтори действие родителя -
		он покажет, что надо сделать.
		*/
		if WasOperatorActiveted { // оператор отреагировал
			purpose := getPurposeGenetic()
			// повторить действия оператора
			purpose.actionID = curActiveActions
			atmzm := createAndRunAutomatizmFromPurpose(purpose)
			if doWritingFile {
				SaveAutomatizm()
			}
			return atmzm
		}
	}
	// else НИЧЕГО НЕ ДЕЛАТЬ: при высокой актуальности - растерянность, при низкой - лень
	return nil
}

////////////////// ОРИЕНТИРОВОКА, если есть автоматизм - ВЫЗЫВАТЕСЯ ВСЕГДА, не только при новых условиях
/*проверить подходит ли автоматизм defAutomatizmID к текущим условиям, если нет,
- по опыту того, к чему приводят новые условия - режим нахождения альтернативы
Или если автоматизма пока не имеет Belief==2, т.е. еще непроверненный

! важно: если вернуло автоматизм, значит хочет попробовать
*/
func orientation_2(nodeAutomatizmID int) *Automatizm {
	lib.WritePultConsol("Простейший ориентировочный рефлекс частичного непонимания (2 типа)")

	//  получение текущего состояния информационной среды: отражение Базового состояния и Активных Базовых контекстов
	GetCurrentInformationEnvironment()

	// оценка опасности ситуации, необходиомсть срочных действий
	veryActualSituation = CurrentInformationEnvironment.veryActualSituation
	// выявить ID парамктров гомеостаза как цели для улучшения в данных условиях
	curTargetArrID = CurrentInformationEnvironment.curTargetArrID

	// обработка автоматизма, рвущегося на выполнение. Есть ли опасная новизна?
	atmzm := getPurposeGenetic2AndRunAutomatizm(nodeAutomatizmID) // в purpose_genetic.go
	if atmzm != nil {
		return atmzm
	}

	return nil
}

____________________________________________

BOT/brain/psychic/psi_information_environment.go
____________________________________________
/* Информационная среда - основа текущего самоощущения

с каждым обновление инф.среды предыдущее запоминается в массиве InformationEnvironmentObjects
и еть OldInformationEnvironment
*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
	"strconv"
)

/////////////////////////////////////////////////////
/* Среда условий текущего состояния - интегративная информационная среда
Образ интегративной информационной среды Формируется временно и не сохранятся в файле.
Объекты InformationEnvironment накапливаются в оперативке, указалеи входят в  эпизодическую память и удаляются во сне.

Структуру информационной среды можно дополнять по мере необходимости, т.к. она не сохраняется в файле.

Заполняться начинает на уровне рефлексов.
*/
type InformationEnvironment struct {
	LifeTime   int  // момент создания кадра инф.окружения
	IsPsyLevel bool // true - данные записаны на стороне психики
	IsSleep    bool // true - организм спит (во сне контекст задает тоже InformationEnvironment) if IsSleeping {

	//устанавливается в func PsychicCountPuls, сбрасывается в func GetAutomotizmActionsString и в infoFunc25()
	IsIdleness100pulse bool // Не было действий Beast более 100 пульсов

	// общая оценка гомео-настроения
	Mood int //сила Плохо -10 ... 0 ...+10 Хорошо
	//ID парамктров гомеостаза как цели для улучшения в данных условиях
	curTargetArrID []int
	// текущая эмоция Emotion, может быть произвольно изменена
	PsyEmotionId int
	// опасность состояния
	danger              bool // получить из GetAttentionDanger
	veryActualSituation bool // оценка важности ситуации, необходиомсть срочных действий

	PsyMood int //Субъективно ощущаемая оценка, текущее осознаваемое настроение, которое можно произвольно изменять.

	// текущий образ сочетания действий с Пульта
	ActionsImageID          int // акция типа ActionsImage
	IsUnknownActionsImageID int //Образ, не имеющий модели понимания, вызывает беспокойство и активирует пассивный режим

	// текущий образ сочетания ОТВЕТНОГО действия мот.автоматизма
	AnswerImageID int // акция типа ActionsImage
	// текущий образ ментального автоматизма
	//MentalAutomatizmID int    //  типа MentalAutomatizm

	/*Это - период ожидания ответа с пульта на действие
	чтобы инфо-функции знали, работают ли они на Стимул или произвольно.
	В случае произвольности размышления о наиболее значащем могут давать действия.
	*/
	IsWaitingPeriod bool // это - период ожидания ответа с пульта на действие.

	/*наиболее важный образ типа extremImportance - хранится в обычном массиве curImportanceObjectArr []extremImportance
	Можно по extremImportance найти importance: getImportanceFromExtremImportance(Stimul int, kind int)
	*/
	ExtremImportanceObjectID int
	// актуальнsq по эффекту Правило, выделенное из ExtremImportanceObjectID в ходе перебора infoFunc5()
	ActualEpisodicMemoryID int
	// текущая Доминанта нерешенной проблемы
	DominantaID int

	//Нужно подумать о проблеме автоматизма или проявить инициативу, в общем, запустить func infoFunc25()
	needThinkingAboutAutomatizm bool

	// true - текущий стмул навязывает то, что не соотвествует текущей Теме и Цели
	isStimulToForce bool
}

/*
	с каждым обновлением инф.среды предыдущее запоминается в массиве

InformationEnvironmentObjects и еще еть OldInformationEnvironment
Кратковременная память кадров ИЕ. В файл не записывается, освобождается во сне.
TODO: Позволяет удобно вспомнить, что было недавно, использовать для выбора сновидения, темы общения и для мечтаний.
В инфо-функции infoFunc10() позволяет вспомнить самое значимое, что было для начала размышения (кроме возврата на прерванное).
*/
var InformationEnvironmentObjects []*InformationEnvironment

func GetInformationEnvironmentObjectsLength() int { // для сна
	return len(InformationEnvironmentObjects)
}

var CurrentInformationEnvironment InformationEnvironment

func initCurrentInformationEnvironment() {

	CurrentInformationEnvironment.ActionsImageID = 0
	CurrentInformationEnvironment.IsUnknownActionsImageID = 0
	CurrentInformationEnvironment.LifeTime = LifeTime

	CurrentInformationEnvironment.ActualEpisodicMemoryID = 0
	CurrentInformationEnvironment.ExtremImportanceObjectID = 0
}

var OldInformationEnvironment InformationEnvironment

//////////////////////////////////////////////////////////////////////
/* создать новй кадр IE из OldInformationEnvironment
и записать его адрес в InformationEnvironmentObjects
*/
func saveOldIE() {
	var ie InformationEnvironment // создан новый реальный объект инфосреды, от которых нужно освобождать память
	ie = OldInformationEnvironment
	InformationEnvironmentObjects = append(InformationEnvironmentObjects, &ie)
	OldInformationEnvironment = CurrentInformationEnvironment
	CurrentInformationEnvironment = ie
}

///////////////////////////////////////

///////////////////////////////////////////////////////
/*  получение текущего состояния информационной среды: отражение Базового состояния и Активных Базовых контекстов
только при ориентировчном рефлексе и осмыслении результатов - обновление самоощущения!
*/
func GetCurrentInformationEnvironment() {
	saveOldIE()

	// !!! 	initCurrentInformationEnvironment() НИЧЕГО НЕ СБРАСЫВАТЬ, ТОЛЬКО ПЕРЕКРЫВАТЬ НОВЫМИ ЗНАЧЕНИЯМИ!

	CurrentInformationEnvironment.LifeTime = LifeTime // момент обновления

	CurrentInformationEnvironment.IsSleep = IsSleeping

	// определение текущего сочетания ID Базовых контекстов - оно есть всегда, даже если ничего не сделано на Пульте - нулевое сочетание.
	bsIDarr := gomeostas.GetCurContextActiveIDarr()
	// текущая эмоция Emotion, может быть произвольно изменена
	CurrentInformationEnvironment.PsyEmotionId, _ = createNewBaseStyle(0, bsIDarr, true)

	CurrentInformationEnvironment.veryActualSituation, CurrentInformationEnvironment.curTargetArrID = gomeostas.FindTargetGomeostazID()

	//ActID:=action_sensor.CheckCurActionsContext()  модификация - только в automatism_tree.go при акции с пульта!
	//CurrentInformationEnvironment.ActionsImageID,_=createNewlastActivityID(0,ActID,true)// текущий образ сочетания действий с Пульта Activity

	if lastAutomatizmRun != nil {
		CurrentInformationEnvironment.AnswerImageID = lastAutomatizmRun.ActionsImageID
	}

	CurrentInformationEnvironment.danger = GetAttentionDanger()
	CurrentInformationEnvironment.Mood = GetCurMood()
	CurrentInformationEnvironment.PsyMood = PsyMood

	writeInformationEnvironmentMarker()
	return
}

///////////////////////////////////////////////////////

// /////////////////////////////
// обновление состояния информационной среды
func refreshCurrentInformationEnvironment() {
	//return  // инфа просто перекрывается новой
	///////// Информационная среда осознания ситуации
	// Нужно собрать всю информацию, которая может повлиять на решение.
	//  получение текущего состояния информационной среды: отражение Базового состояния и Активных Базовых контекстов
	GetCurrentInformationEnvironment()

	// оценка опасности ситуации, необходиомсть срочных действий
	veryActualSituation = CurrentInformationEnvironment.veryActualSituation
	// выявить ID парамктров гомеостаза как цели для улучшения в данных условиях
	curTargetArrID = CurrentInformationEnvironment.curTargetArrID

	/* Еще информация:
	жизненный опыт  psy_Experience.go
	доминанта psy_problem_dominanta.go
	субъектиная оценка ситуации для применения произвольности
	*/

	// актуальной инфой являются узлы активной ветки дерева понимания, особенно контекст SituationID
}

///////////////////////////////////////////////

// ////////////////////////////////////////////////////
// записать метку изменения information_environment при каждом обновлении
func writeInformationEnvironmentMarker() {
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/self_perception_count.txt")
	var old = 0
	if strArr != nil {
		old, _ = strconv.Atoi(strArr[0])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/self_perception_count.txt", strconv.Itoa(old+1))
}

//////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/PultGetSelfPerceptionInfo.go
____________________________________________
/* Для Пульта выдать текущее состояние Самоощещения.

 */

package psychic

import (
	"BOT/brain/gomeostas"
	"strconv"
)

///////////////////////////////////////

// информация самоощущения для Пульта
func GetSelfPerceptionInfo() string {

	//!!! refreshCurrentInformationEnvironment() инфа просто перекрывается новой
	// опасность
	danger := GetAttentionDanger()
	// актуальность ситуации
	veryActualSituation, _ := gomeostas.FindTargetGomeostazID()

	// против паники типа "одновременная запись и считывание карты"
	//	if notAllowScanInTreeThisTime{
	//		return "!!!"
	//	}
	ie := CurrentInformationEnvironment
	// нужно задать постоянную высоту блока чтобы он не мигал по высоте <div style='min-height:230px;'>
	var out = "<div style='min-height:230px;'><br>Общее состояние жизненных параметров: <b>" //background-color:#FFEBFC;
	if gomeostas.CommonBadNormalWell == 1 {
		out += "ПЛОХО"
	}
	if gomeostas.CommonBadNormalWell == 2 {
		out += "НОРМА"
	}
	if gomeostas.CommonBadNormalWell == 3 {
		out += "ХОРОШО"
	}
	out += "</b>"
	////////////////////////////
	out += "<br>Ощущаемое настроение: <b>"
	if ie.PsyMood == -1 {
		out += "Плохое (" + strconv.Itoa(ie.Mood) + ")"
	}
	if ie.PsyMood == 0 {
		out += "Нормальное (" + strconv.Itoa(ie.Mood) + ")"
	}
	if ie.PsyMood == 1 {
		out += "Хорошее (" + strconv.Itoa(ie.Mood) + ")"
	}
	out += "</b>"
	////////////////////////////////
	out += "<br>Текущая эмоция: " + GetCurrentEmotionReception() //getEmotonsComponentStr(ie.PsyEmotionImg)
	///////////////////////////////
	out += "<br>Опасность состояния: <b>"
	if danger {
		out += "Опасное"
	} else {
		out += "Неопасное"
	}
	out += "</b>"
	out += "<br>Важность состояния: <b>"
	if veryActualSituation {
		out += "Очень важное состояние"
	} else {
		out += "Спокойное состояние."
	}
	out += "</b>"

	switch idlenessType {
	case 0:
		out += "<br><span style='color:#888888'>Лень: <b>отсуствует</b></span>"
	case 1:
		out += "<br>Лень: <b>гомеостатическая</b>"
	case 2:
		out += "<br>Лень: <b>осознанная лень</b>"
	}

	////////////////////////////////
	if ie.ActionsImageID > 0 {
		str := GetActionsString(ie.ActionsImageID)
		out += "<br>Текущий образ сочетания действий с Пульта: <b>" + str + "</b>"
	} else {
		out += "<br>текущий образ сочетания действий с Пульта: отсуствует"
	}
	////////////////////////////////

	out += "<br>Субъективно ощущаемая оценка, текущее осознаваемое настроение: <b>" + strconv.Itoa(ie.PsyMood) + "</b><br>"

	if ie.AnswerImageID > 0 {
		str := GetActionsString(ie.AnswerImageID)
		out += "<br>Текущий образ сочетания ОТВЕТНОГО действия мот.автоматизма: <b>" + str + "</b>"
	} else {
		out += "<br>текущий образ сочетания ОТВЕТНОГО действия мот.автоматизма: отсуствует"
	}

	if ie.ExtremImportanceObjectID > 0 {
		//oi, ok := importanceFromID[ie.ExtremImportanceObjectID]
		oi := getExtremObjFromID(ie.ExtremImportanceObjectID)
		if oi != nil {
			out += "<br>Нет объекта importanceTypeName </span>"
		} else {
			out += "<br>Наиболее важный образ типа extremImportance: " + onClickStr(1, "show_object", "")
		}
	}
	out += "<br>Текущая Доминанта нерешенной проблемы: " + onClickStr(ie.DominantaID, "show_dominant", "")

	// TODO остальное

	out += "</div>"
	return out
}

///////////

____________________________________________

BOT/brain/psychic/purpose_genetic.go
____________________________________________
/*   Определение Цели в данной ситуации - ну уровне наследственных функций
исходя из текущей информационной среды CurrentInformationEnvironment:

Эти безусловные рефлексы психики прописываются в виде функций.

Генетические цели действий Beast ID гомео-параметров, которые призвано улучшить данное действие - по его ID
прописана в terminal_actons.txt (http://go/pages/terminal_actions.php)
var TerminalActionsTargetsFromID=make(map[int][]int)
*/

package psychic

import (
	TerminalActions "BOT/brain/terminete_action"
	"BOT/lib"
	"sort"
)

///////////////////////////////////////////////

/*
	образ цели бессловестного действия Формируется временно и не сохранятся в файле

Объекты PurposeGeneticObject накапливаются в оперативке и удаляются во сне
*/
type PurposeGenetic struct {
	puls       int           // PulsCount
	veryActual bool          // true - цель очень актуальна
	targetID   []int         //массив ID парамктров гомеостаза как цели для улучшения в данных условиях
	actionID   *ActionsImage //выбранный образ действия для данной цели
	// для каждого actionID сила действий сначала принимается =5, а потом корректируется мозжечковыми рефлексами
}

var PurposeGeneticObject []*PurposeGenetic

// текущая цель сохраняется до перекрытия следующим orientation_N()
var CurrentPurposeGenetic PurposeGenetic
var OldPurposeGenetic PurposeGenetic // OldPurposeGenetic=CurrentPurposeGenetic
///////////////////////////////////////

/*
Определение Цели в данной ситуации - на уровне наследственных функций
*/
func getPurposeGenetic() *PurposeGenetic {
	var pg PurposeGenetic
	pg.puls = PulsCount
	pg.veryActual = veryActualSituation
	pg.targetID = curTargetArrID

	/*Сначала посмотреть подходит ли по условиям текущий безусловный или условный рефлекс и сделать автоматизм по его действиям
	чтобы проверить его в текущих условиях т.к. getPurposeGenetic() срабатывает по ориентировочному рефлексу.
		При этом уже не будет формироваться условный рефлекс при осознанном внимании
	(т.к. заблокируется выработанным пробным действием)
	*/
	//есть ли подходящий по условиям безусловный или условный рефлекс и сделать автоматизм по его действиям
	if len(actualRelextActon) > 0 {
		_, actId := CreateNewlastActionsImageID(0, 0, actualRelextActon, nil, 0, 0, true)
		pg.actionID = actId
	} else {
		/* этого практивески не может быть, потому, что если нет рефлексов,
		   		то дейсвтвия в GetActualReflexAction() берутся из эффектов действий
		   НО если вообще нет лействий, то остается только случайное действие или бездействие
		   Так что лучше не заморачиваться с этим!
		*/
		// veryActualSituation: плохо для  1, 2, 7 и/или 8  параметров гомеостаза
		//if veryActualSituation { // нужно хоть что-то сделать, ПАНИКА
		ActID := []int{21} // паника
		_, atmz := CreateNewlastActionsImageID(0, 0, ActID, nil, 0, 0, true)
		pg.actionID = atmz
		//}
	}

	PurposeGeneticObject = append(PurposeGeneticObject, &pg)
	OldPurposeGenetic = CurrentPurposeGenetic
	CurrentPurposeGenetic = pg
	savePurposeGenetic = &pg
	return &pg
}

/////////////////////////////////////////////////////////

// atmzm :=createAndRunAutomatizmFromPurpose(purpose)

func createAndRunAutomatizmFromPurpose(purpose *PurposeGenetic) *Automatizm {
	atmzm := createAutomatizm(purpose)
	// запустить автоматизм

	// в automatizm_result.go после оценки результата будет осмысление с активацией Дерева Понимания
	return runAutomatizmFromPurpose(atmzm, purpose)
}

// ////////////////////////////////////////////////////////
// запуск автоматизма на выполнение с сохранением текущего состоняия гомеостатической цели в savePurposeGenetic
var curPurposeGeneticAutmtzm *PurposeGenetic

func runAutomatizmFromPurpose(atmzm *Automatizm, purpose *PurposeGenetic) *Automatizm {
	// запустить автоматизм
	curPurposeGeneticAutmtzm = purpose
	if RumAutomatizm(atmzm) {
		// отслеживать последствия в automatizm_result.go
		// setAutomatizmRunning(atmzm, purpose)
		// в func RumAutomatizm   setAutomatizmRunning(am, curPurposeGeneticAutmtzm)
	}
	curPurposeGeneticAutmtzm = nil
	// в automatizm_result.go после оценки результата будет осмысление с активацией Дерева Понимания
	return atmzm
}

//////////////////////////////////////////////////////////

// выбрать из ранее удачного автоматизма, перекрыть цель новой и запустить новый автоматизм
func chooseAutomatizmSuccessAndRun(purpose *PurposeGenetic) *Automatizm {
	// ранее найденные удачные автоматизмы
	//  AutomatizmSuccessFromIdArr[n].GomeoIdSuccesArr[] - какие ID гомео-параметров улучшает это действие
	for _, v := range AutomatizmSuccessFromIdArr {
		if v == nil {
			continue
		}
		targID := v.GomeoIdSuccesArr
		for i := 0; i < len(targID); i++ {
			if lib.ExistsValInArr(purpose.targetID, targID[i]) {
				// первый попавшися
				// создать новый автоматизм на основе успешного, но для данных условий и запустить его
				// TODO !не проверено!
				purpose.targetID = nil
				purpose.targetID = append(purpose.targetID, targID[i])
				// вытащить действия автоматизма
				//				trigID := ActionsImageArr[v.ActionsImageID]
				trigID, ok := ReadeActionsImageArr(v.ActionsImageID)
				if ok {
					purpose.actionID = trigID
					atmzm := createAndRunAutomatizmFromPurpose(purpose)
					return atmzm
				}
			}
		}
	}
	return nil
}

///////////////////////////////////////////////////////

/*
	пробовать всякие случайныее простые действия, не повторяясь

Выдавая это на стадии 3, тварь получает реакцию оператора, которую отзеркаливает
*/
var usedActIdArr []int   // какие деййствия уже были испробованы, погасить во сне wakingUp()
var UsedPraseIdArr []int // буфер уже опробованных фраз, чтобы не повторяться.
func findAnySympleRandActions() *Automatizm {

	// выдать массив возможных действий по ID парамктров гомеостаза как цели для улучшения в данных условиях
	targID, actID := TerminalActions.GetSimpleActionForCurContitions()
	// удалить уже использованное
	var tmp []int
	for i := 0; i < len(actID); i++ {
		if !lib.ExistsValInArr(usedActIdArr, actID[i]) {
			tmp = append(tmp, actID[i])
		}
	}
	actID = tmp

	if len(actID) > 0 {
		var actArrId []int
		if len(actID) > 2 { // сделать случайное сочетание
			actArrId1 := lib.RandChooseIntArr(actID)
			actArrId = append(actArrId, actArrId1)
			actArrId2 := lib.RandChooseIntArr(actID)
			actArrId = append(actArrId, actArrId2)
			actArrId = lib.UniqueArr(actArrId)

		} else {
			actArrId1 := lib.RandChooseIntArr(actID)
			actArrId = append(actArrId, actArrId1)
		}
		// чтобы не повторяться
		for i := 0; i < len(actArrId); i++ {
			usedActIdArr = append(usedActIdArr, actArrId[i])
		}

		var purpose PurposeGenetic
		purpose.targetID = targID
		_, trig := CreateNewlastActionsImageID(0, 0, actArrId, nil, 0, 0, true)
		purpose.actionID = trig
		atmzm := createAndRunAutomatizmFromPurpose(&purpose)
		return atmzm
	}

	// если кончились действия, начали порверять фразы  ДЛЯ ПОПОЛНЕНИЯ type Verbal struct (verbalFromIdArr[])
	// проверяем вербальные образы начиная от последнего, самого свежего - так меньше будет переборов во второй стадии
	keys := make([]int, 0, len(VerbalFromIdArr))
	for k, v := range VerbalFromIdArr {
		if v == nil {
			continue
		}
		keys = append(keys, k)
	}
	sort.Sort(sort.Reverse(sort.IntSlice(keys)))
	for _, k := range keys {
		if lib.ExistsValInArr(UsedPraseIdArr, k) {
			continue
		}
		UsedPraseIdArr = append(UsedPraseIdArr, k)
		//val:=VerbalFromIdArr[k]
		val, ok := ReadeVerbalFromIdArr(k)
		if !ok {
			continue
		}
		var purpose PurposeGenetic
		purpose.targetID = targID
		//!? При создании нового автоматизма с фразой вписывать Tnn: тон настроение, которое брать из текущего гомеостаза ?
		/*if IsGameMode{
			if action_sensor.IsPress3or4button{ // наказать или поощрить
				action_sensor.IsPress3or4button=false
				return nil
			}
		}*/
		/*[В игровом режиме не давать нижнему коду создавать автоматизмы, получается мусор]
		это не совсем верно: нужно дать возможность на действие кнопки создать автоматизм по рефлексу, тогда и не будет "мусора" - не адекватной привязки действий в автоматизме
		Это и сделано в getPurposeGeneticAndRunAutomatizm() - добавлено условие для IsGameMode не вербальных действий Оператора*/
		actReflex := actualRelextActon
		_, trig := CreateNewlastActionsImageID(0, 0, actReflex, val.PhraseID, val.ToneID, val.MoodID, true)
		purpose.actionID = trig
		atmzm := createAndRunAutomatizmFromPurpose(&purpose)
		if atmzm.Usefulness < 0 { // при попытке создать - вернул старый плохой автоматизм
			continue // продолжить поиск в VerbalFromIdArr
		}
		// сразу запускать вебальный автоматизм - это лучшая стратегия.
		return atmzm
	}
	return nil
}

____________________________________________

BOT/brain/psychic/purpose_genetic_orient1.go
____________________________________________
/*
Для ориентировочного рефлекса типа 1
функции для определения Цели в данной ситуации - на уровне наследственных функций
исходя из текущей информационной среды CurrentInformationEnvironment:

*/

package psychic

import (
	"BOT/brain/gomeostas"
)

/*
	Определение Цели в данной ситуации - на уровне наследственных функций

Здесь getPurposeGenetic2AndRunAutomatizm.
*/
func getPurposeGeneticAndRunAutomatizm() *Automatizm {
	var flgBlockAtmz bool // флаг блокировки автоматизма

	purpose := getPurposeGenetic() // выбираются наиболее подходящие действия для автоматизмаы
	// мозжечковые рефлексы - самый первый уровень осознания - подгонка действий под заданную Цель.

	// нужно ли вообще шевелиться?
	// veryActualSituation: плохо для  1, 2, 7 и/или 8  параметров гомеостаза
	// на 2 стадии нужно добиться привязки к кнопкам автотизмов,  созданные по рефлексам (включая условные) - иначе на следующих стадиях будет много мусора при использвании кнопок в качестве поощрений/нказаний
	// это одна из причин, почему не стоит перескакивать стадии. Чем проще автоматизм, тем на более ранней стадии он должен быть сформирован.
	if purpose.veryActual || flgConditionReflexes == true || curActiveVerbalID == 0 {
		if purpose.actionID.ID > 0 && curActions.ActID != nil {
			/* сформировать пробный автоматизм моторного действия и сразу запустить его в действие
			Зафиксироваь время действия
			20 пульсов следить за измнением состояния и ответными действиями - считать следствием действия
			оценить результат и скорректировать силу мозжечком в записи автоматизма.
			Выбрать любое действие, т.к. послед создания автоматизма в данной ветке detectedActiveLastNodID
			он уже не вызовет orientation_1(), а будет orientation_2()
			*/
			atmzm := createAndRunAutomatizmFromPurpose(purpose)
			if atmzm.Usefulness >= 0 {
				if doWritingFile {
					SaveAutomatizm()
				}
				return atmzm
			} else {
				flgBlockAtmz = true
			}
		}
		/* нет действий (практически невозможная ситуация потому, что если нет рефлексов,
		то дейсвтвия в GetActualReflexAction() берутся из эффектов действий)
		*/
		if purpose.actionID == nil || flgBlockAtmz { // AutomatizmSuccessFromIdArr=make(map[int]*Automatizm)
			// ранее найденные удачные автоматизмы
			// выбрать из ранее удачного автоматизма, перекрыть цель новой и запустить новый автоматизм
			atmzm := chooseAutomatizmSuccessAndRun(purpose)
			if atmzm != nil {
				return atmzm
			}
			// нет действий, попробовать бессмысленно выдать фразы имеющиеся Вериниковские раз нужно что-то срочно делать
			if purpose.veryActual {
				// подобрать хоть как-то ассоциирующуюся фразу из имеющизся
				phraseID := findSuitablePhrase()
				if len(phraseID) > 0 {
					purpose.actionID.PhraseID = phraseID
					atmzm := createAutomatizm(purpose)
					// запустить автоматизм
					if RumAutomatizm(atmzm) {
						// отслеживать последствия в automatizm_result.go
						setAutomatizmRunning(atmzm, purpose)
					}
					// в automatizm_result.go после оценки результата будет осмысление с активацией Дерева Понимания
					if atmzm != nil {
						return atmzm
					}
				}
			}
		}
		//  ЗДЕСЬ активировать Дерево Понимания НЕ НУЖНО, действие уже запущено, омысление будет по результату.
	} else { // нет атаса, можно спокойно поэкспериментивроать, если есть любопытсво
		if gomeostas.BaseContextActive[2] || gomeostas.BaseContextActive[3] { // активен Поиск или Игра
			// тупо метод тыка
			// Тупо поэкспериментировать для пополнения опыта (не)удачных автоматизмов
			// TODO !не проверено!
			// в отличии от createAndRunAutomatizmFromPurpose(purpose) не использовать текущие рефлексы, а пробовать всякое
			// Выдавая это на стадии 3, тварь получает реакцию оператора, которую отзеркаливает
			atmzm := findAnySympleRandActions()
			if atmzm != nil {
				return atmzm
			}
		}
	}
	// НЕ ИГРА  И НЕ ПОИСК, нет атаса, пониженная мотивация что-то делать если нет актуальности
	if EvolushnStage == 2 {
		// нет действий, попробовать использовать AutomatizmSuccessFromIdArr.GomeoIdSuccesArr
		// выбрать из ранее удачного автоматизма, перекрыть цель новой и запустить новый автоматизм
		atmzm := chooseAutomatizmSuccessAndRun(purpose)
		return atmzm
	}
	// ЛЕНЬ
	return nil
}

____________________________________________

BOT/brain/psychic/purpose_genetic_orient2.go
____________________________________________
/*   
Для ориентировочного рефлекса типа 2
функции для определения Цели в данной ситуации - на уровне наследственных функций
исходя из текущей информационной среды CurrentInformationEnvironment:

*/

package psychic

import (
	"BOT/brain/gomeostas"
)

///////////////////////////////////////////////
// обработка автоматизма, рвущегося на выполнение, но в условиях может быть новизна news
/* Здесь - очень органиченные возможности адаптации автоматизма:
плохой - не выполнять, хороший - выполнять
при опасной ситуации выполнять тот, какой есть,
при спокойной ситуации - пробовать рефлексы мозжечка.

Из-за столь скудных возможностей и разросся функционал мыслительных автоматизмов
с их произвольностью (- перекрытием имеющихся автоматизмов новыми).
 */
var oldNodeAutomatizm=0 // прошлы раз запускался такой штатный автоматизм
func getPurposeGenetic2AndRunAutomatizm(atmtzmID int)(*Automatizm){

	//atmzm:= AutomatizmFromId[atmtzmID]
	atmzm,ok:=ReadeAutomatizmFromId(atmtzmID)
	if !ok {
		return nil
	}

	// Определение Цели в данной ситуации - на уровне наследственных функций
	purpose:=getPurposeGenetic()
	// мозжечковые рефлексы - самый первый уровень осознания - подгонка действий под заданную Цель.

	// есть ли очень значимые новые признаки?
	newsRes:=getImportantSigns()
	if newsRes { // повышенная опасность от оператора
		// срочность и важность ситуации: если очень срочно и важно - просто оставить имеющийся автоматизм
		runAutomatizmFromPurpose(atmzm, purpose)
		return atmzm
	}

	if purpose.veryActual{// нужно ли вообще шевелиться?
	// высокий purpose.veryActual, 	нужно выполнить штатный автоматизм, а не придуманный

	// если в прошлый раз уже был такой автоматизм, то ничего не делать, иначе начинает повторять одно и то же
if oldNodeAutomatizm != atmzm.ID {
	runAutomatizmFromPurpose(atmzm, purpose)
	oldNodeAutomatizm = atmzm.ID
	return atmzm
}

	// список всех автоматизмов для ID узла Дерева
	//aArr:=GetMotorsAutomatizmListFromTreeId(detectedActiveLastNodID)

	/*aID := getAutomatizmFromNodeID(detectedActiveLastNodID)
	AutomatizmFromIdMapCheck()
	atmzm=AutomatizmFromId[aID]
	purpose.actionID=ActionsImageArr[atmzm.ActionsImageID]
	runAutomatizmFromPurpose(atmzm, purpose)*/
	return nil

//if purpose.veryActual
}else {              // нет опасности и нет опасной новизны

		/*/ плохой автоматизм,
		if atmzm.Usefulness == -1 {	// сделать пользу ==0 и запустить с повышенным уровнем
			// была ли уже оптимизация?
			if cerebellumCoordination(atmzm, 0) {
				atmzm.Usefulness=0 // чтобы не блокировался
				runAutomatizmFromPurpose(atmzm, purpose)
				return atmzm
			} else {
				if gomeostas.BaseContextActive[2] || gomeostas.BaseContextActive[3] { // активен Поиск или Игра
					// тупо метод тыка
					// Тупо поэкспериментировать для пополнения опыта (не)удачных автоматизмов
					// TODO !не проверено!
					// в отличии от createAndRunAutomatizmFromPurpose(purpose) не использовать текущие рефлексы, а пробовать всякое
					// Выдавая это на стадии 3, тварь получает реакцию оператора, которую отзеркаливает
					atmzm := findAnySympleRandActions()
					return atmzm
				} else { // НЕ ИГРА  И НЕ ПОИСК, плохой автоматизм просто не выполнять
					return nil
				}
			}
		}*/
		if atmzm.Usefulness < 0 {// совсем плохой автоматизм
			if gomeostas.BaseContextActive[2] || gomeostas.BaseContextActive[3] { // активен Поиск или Игра
				atmzm := findAnySympleRandActions()
				return atmzm
			}
		}

		//все нормально, просто выполнить автоматизм и отслеживать последствия
		runAutomatizmFromPurpose(atmzm, purpose)
		return atmzm
	}

	return nil
}
/////////////////////////////////////////////////////////////////
____________________________________________

BOT/brain/psychic/sleep_process.go
____________________________________________
/*  обработка во сне

Во время сновидений мы можем видеть и слышать вещи,
которые не существуют в реальном мире, испытывать эмоции и переживать события,
которые могут быть несвязанными с нашей повседневной жизнью.

Наиболее известным примером был его сон о гильотине: длинный сон о французской революции,
кульминацией которого был приговор Мори к смертной казни. Он проснулся в момент падения ножа гильотины,
обнаружив, что доска с изголовья кровати упала ему на шею.
Внешний раздражитель запустил последовательность воспоминаний, которые образовали сновидения.

Освобождать неиспользуемую память: v=nil

в understanding.go есть func WakeUpping()
*/

package psychic

/*
	можно ли заснуть и спать - вызывается из sleep.SleepPuls()

Если хочется спать
*/
func IsPossibleToSleep() bool {
	/*
		//Если уже сон:  НЕ ПРОСЫПАТЬСЯ ОТ ПСИХ СТИМУЛОВ!
		if IsSleeping {
			// нужно проснуться т.к. при dreamingEpisodeHistoryID в цикле возникла высокая значимость опасности
			if true{
				return false
			}
		}else{// можно ли заснуть?
	*/
	if extremImportanceObject != nil && extremImportanceObject.extremVal <= 5 {
		return false // нельзя засыпать, мучиться негативной проблемой
	}
	if CurrentInformationEnvironment.veryActualSituation ||
		CurrentInformationEnvironment.danger {
		return false // нельзя засыпать, опасно
	}

	return true // да можно заснуть или продолжать спать
}

////////////////////////////////////////////

func sleepingProcess() {
	// удаление временных массивов пробных действий (отрицательные значяения индеков) - во сне
	RemoveTemporeryTryActionArr()

	// элементы кратковременной памяти:
	InterruptMemory = nil
	saveBaseLinksCicleSynthesis = nil
	SynthesizeImportanceMemory = nil

	preparePurposeGeneticObject()
	prepareInformationEnvironment()
	prepareEpisodicMenory()
	//prepareImportanceObjectMenory() - просто очистить при просыпании

	lookForUnactualDominant() // просмотрт доминант для закрытия неактуальных
}

//////////////////////////////////////////////

// ////////////////////////////////////////////////////
func preparePurposeGeneticObject() {
	wCount := len(PurposeGeneticObject)
	if wCount == 0 {

	}
}

////////////////////////////////////////////////////////
/*т.к. объекты хранятся до Сна, то после обработки инфа должна перейти
в другие структуры (предположительные автоматизмы)

// реальный объект инфосреды, от которых нужно освобождать память
*/
func prepareInformationEnvironment() {
	// начать смотреть с начала, не доходя до последних элементов, записываемых во сне
	for i := 0; i < len(InformationEnvironmentObjects); i++ {
		// TODO
		//v=nil // освободить память просмотренного
	}
	// м.б. при просыпании просто забыть всю информацию ?
	//InformationEnvironmentObjects=nil

}

////////////////////////////////////////////////////////
/*  обработка в сновидениях, редуцирование СТАРЫХ использованных эпизодов
будут запоминаться только ВАЖНЫЕ эпизоды или последние 10000 эпизодов
*/
func prepareEpisodicMenory() {

}

//////////////////////////////////////////////////////////

/*
 */
func prepareImportanceObjectMenory() {

}

////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/trigger_effect_mental.go
____________________________________________
/* Образ ментального Правила.
Ментальное Правило используется как опыт использования ментальных автоматизмов для нахождения решения.

Начало фиксации Правила - ментальный запуск моторного автомтаизма (MentalAutomatizm.ActionsImageID ->  activateMotorID).
Эффект Правила отражает насколько мент.автоматизм привел к успешному решению,
т.е. оценивается в момент действий оператора в период ожидания.
После этого в эпизод память и MentalCyckleEffectArr записывается новое Правило.
Так же при этом проставляется MentalAutomatizm.Usefulness автоматизму, запустившему действие (вообще MentalAutomatizm нивелируется Правилами).

При ментальном запуске моторного автомтаизма фиксируется фрагмент Кратковременной памяти (infoFuncSequence),
начиная с последней объективной активации consciousnessElementary т.е. только цепочка для данной активности дерева актоматизмов,
но могут быть разные активности дерева понимания из-за произвольной активации
(т.к. при переактивации деревьев могут измениться базовые циклы) и эти звенья оцениваются как успешнвые или нет.
Это Правило записывается в массив Правил rulesMentalArr, и в кадр эпиз.памяти с Type=1 .

*/
package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/////////////////////////
/* В отличие от объективнх Правил здесь нет пошаговой сверки с реакцией Оператора (это внутренний цикл поиска решения),
так что нужно просто смотреть наиболее подходящее Правило.
Каждое Правило - один из усвоенных алгоритмов поиска решений - циклом мышления.
Удачный цикл мышления записывается в мент.автоматизм, привязанный к ветке древа проблем.
Так что в ветке проблем фиксируется удачное ментальное Правило == цикл мышления.

funcID []int последовательность ID выполненных инфо-функций до срабатывания автоматизма по func infoFunc17,
т.е. была такая последовательность пока не сработал мот.автоматизм,ч то привело к эффекту Effect.
*/
type MentalCyckleEffect struct {
	ID     int
	funcID []int // infoFuncSequence[]int данной активации - последовательность ID выполненных инфо-функций
	Effect int   // эффект от действий накапливается при каждой новой перезаписи и используется уже суммарное значение.
}
////////////////////////
var MentalCyckleEffectArr =make(map[int]*MentalCyckleEffect)
var MapGwardMentalCyckleEffectArr=lib.RegNewMapGuard()
//////////////////////////////////////////




////////////////////////////////////////////////
// создать новый эффект ментального правила если такого еще нет
var lastMentalCyckleEffectID =0
func createNewlastMentalCyckleEffectID(id int,ShortTermMemoryID []int,Effect int,CheckUnicum bool)(int,*MentalCyckleEffect){
	if Effect<0{Effect=-1}
	if Effect>0{Effect=1}

	if CheckUnicum {
		oldID,oldVal:= checkUnicumMentalCyckleEffect(ShortTermMemoryID,Effect)
		if oldVal!=nil{
			return oldID,oldVal
		}
	}

	if id==0{
		lastMentalCyckleEffectID++
		id= lastMentalCyckleEffectID
	}else{
		//		newW.ID=id
		if lastMentalCyckleEffectID <id{
			lastMentalCyckleEffectID =id
		}
	}

	var node MentalCyckleEffect
	node.ID = id
	node.funcID = infoFuncSequence
	node.Effect=Effect

	lib.MapCheckWrite(MapGwardMentalCyckleEffectArr)
	MentalCyckleEffectArr[id]=&node
	lib.MapFree(MapGwardMentalCyckleEffectArr)

	if doWritingFile{
		SaveMentalCyckleEffectArr() }

	return id,&node
}
func checkUnicumMentalCyckleEffect(ShortTermMemoryID []int,Effect int)(int,*MentalCyckleEffect){
	lib.MapCheckBlock(MapGwardMentalCyckleEffectArr)
	for id, v := range MentalCyckleEffectArr {
if v==nil{continue}
		if !lib.EqualArrs(ShortTermMemoryID,v.funcID) {
			continue
		}
		if Effect != v.Effect {
			continue
		}
		lib.MapFree(MapGwardMentalCyckleEffectArr)
		return id,v
	}
	lib.MapFree(MapGwardMentalCyckleEffectArr)
	return 0,nil
}
/////////////////////////////////////////






//////////////////// сохранить Образы стимула (действий оператора) - ответа Beast
//В случае отсуствия ответных действий создается ID такого отсутсвия, пример такой записи: 2|||0|0|
// ID|ActID через ,|PhraseID через ,|ToneID|MoodID|
func SaveMentalCyckleEffectArr(){
	var out=""
	lib.MapCheckBlock(MapGwardMentalCyckleEffectArr)
	for k, v := range MentalCyckleEffectArr {
	if v==nil{continue}
		out+=strconv.Itoa(k)+"|"
		for i := 0; i < len(v.funcID); i++ {
			if i>0{out+=","}
			out+=strconv.Itoa(v.funcID[i])
		}
		out+="|"
		out+=strconv.Itoa(v.Effect)
		out+="\r\n"
	}
	lib.MapFree(MapGwardMentalCyckleEffectArr)
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/trigger_and_actions_mental.txt",out)

}
////////////////////  загрузить образы стимула (действий оператора) - ответа Beast
// ID|ActID через ,|PhraseID через ,|ToneID|MoodID|
func loadMentalCyckleEffectArr(){
	MentalCyckleEffectArr =make(map[int]*MentalCyckleEffect)
	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/trigger_and_actions_mental.txt")
	cunt:=len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		p:=strings.Split(strArr[n], "|")
		id,_:=strconv.Atoi(p[0])

		s:=strings.Split(p[1], ",")
		var ShortTermMemoryID[] int
		for i := 0; i < len(s); i++ {
			si,_:=strconv.Atoi(s[i])
			ShortTermMemoryID=append(ShortTermMemoryID,si)
		}
		Effect,_:=strconv.Atoi(p[2])
var saveDoWritingFile= doWritingFile; doWritingFile =false
		createNewlastMentalCyckleEffectID(id,ShortTermMemoryID,Effect,false)
doWritingFile =saveDoWritingFile
	}
	return

}
///////////////////////////////////////////


____________________________________________

BOT/brain/psychic/understanding.go
____________________________________________
/*
Описание принципов:
Про циклы мышления Beast fornit.ru/66140
Схема процесса осознания Beast fornit.ru/66141

По каждому стимулу активируется функция 1 и2 уровней осознания:
func consciousnessElementary()
Если остается проблема с решением, как ответить, то запускаются циклы мышдения:
func consciousnessThinking(cID int)

С каждым новым стимулом появляется свой цикл мышления типа type cycleInfo struct
а уже имеющиеся становятся неглавными (фоновыми, неосознаваемыми).
Все циклы вызываются с помощью диспетчера циклов func dispetchConsciousnessThinking(),
проходя поочередно. Очередь запускается по каждому пульсу.
*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
	"sort"
	"strconv"
)

// ////////////////////////////
// для тестирования режим: проверка только третьего уровня, без вмешательства первых
var only3ltvelMind = false

// для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ
var levelOfRunAutomatizm = 0

// only3ltvelMind =true

//////////////////////////////////

// при включении и просыпании - 1 раз, в том числе при просыпении от Сторожевого стимула
var IsFirstActivation = true // первый вызов consciousnessElementary при включении или просыпании -> false
func WakeUpping() { // из sleep/sleep_process.go
	prepareImportanceObjectMenory() // очистить все
	//	IsFirstActivation =true  // только что проснулся - есть в func PsychicCountPuls(
	ReadiStatus = 1 //готовность Beast Для пульта:
}

// сбор строк о проессе осмысления дял Пульта
var conscienceStatus = "" // начинается с каждой объективной активацией вписывается в начало лога cycle.log

//////////////////////////////////////////////

// сколько раз был стимул от оператора после последнего запуска Ответа
var stimulCount = 0

// /////////////////////////////////////////////////////////
var idlenessType = 0 //0 - нет лени, 1 - гомео-лень, 2- осознанная лень Только для вывода на Пульт

var oldThemeImageType = 0 // старый Type образа темы

//var cikleExtremImportanceObject *extremImportance // объект внимания в цикле (перекрывается новым, очистка - при запуске цикла)

/*
	automatizmStatus значения:

0 - сброс рассматривания автоматизма
1 - если автоматизм заблокирован
2 - нет автоматизма или есть старый автоматизм, например, при игнорировании Ответа оператором
*/
var automatizmStatus = 0 // для передачи действий в новый цикл ()

var atmtzmActualTreeNode *Automatizm // отслеживание штатного автоматизма
var atmtzmActualTreeNodeID = 0

var mentAtmzmActualFuncs []int // из функции getFavoritInfoFunc() - выдать номера инфо-фукнции, привычные для данных условий

var isConfusion = false //не было моторного ответа на прошлый стимул, а уже последовавл новый

var show_all_logs = true //false - скрывать холостые, true - показывать все сообщения лога

/*
	эффект от запуска автоматизма сначала == его успешности и меняется в периоде ожидания при Стимуле.

От этого зависит активность мыслей:
при позитивном эффект нет необходимость искать новые действия.
при негативном (после ответа оператора) - нужно искать новые решения.

	if motorActionEffect>0 && c.dreaming==0{// не нужно искать решение, раз уже был нормальный ответ
		return false
	}
*/
var motorActionEffect = 0

//////////////////////////////////////////////////////

/////////////////////////////////////////////////////
/* Главная, активная с каждым ориентировочным рефлексом функция циклов осмысления
для поддержания информационной среды и произвольности.

*/
func consciousnessElementary() bool { // return true

	//levelOfRunAutomatizm = 0

	if EvolushnStage < 4 {
		return false
	}

	mentalInfoStruct.noOperatorStimul = false

	// первый проход - при включении и при просыпании func WakeUpping() из sleep/sleep_process.go
	if IsFirstActivation {

		IsFirstActivation = false
		initMentalMemories()
		clinerCycleLogsFiles()
	} else { //if IsFirstActivation  {
		/* Не дергать func consciousnessElementary()
		при незначительных изменениеях гомео-параметров
		*/
		if ActivationTypeSensor == 1 { // активация по изменению гомео-параметров
			// насколько изменились параметры
			if !gomeostas.GetGomeoParsDiff() {
				return false // недостаточно сильное изменение, чтобы привлечь внимание
			}
			if (PulsCount - curActiveActionsPulsCount) > 10 { // прошло > 10 пульсов со времени последнего стимула от оператора
				/* но измение гомео-параметров не могло произойти из-за стимула оператора
				так что можно включать режим провокации
				*/
				mentalInfoStruct.noOperatorStimul = true // не было стимула от оператора > 10 сек при значительном изменении гомео-параметров
			}
		}
	}

	lib.NoReflexWithAutomatizm = false // пока еще можно показывать акции рефлексов с автоматизмами в одной плашке
	///////////////////////////////////////////////////////////

	/* освободить запрет на запуск автоматизма (обычно запущенного ментально)
	wasRunTreeStandardAutomatizm=true устанавливается при любом запуске автоматизма в func RumAutomatizm
	ТЕПЕРЬ всегда после запуска автоматизма LastRunAutomatizmPulsCount >0
	*/
	//wasRunTreeStandardAutomatizm = false

	//initInfoFunc8pars()

	if IsSleeping { // - в процессе сна без сновидения
		return false
	}
	endDereamsCycles(true) // погасить все главные циклы дремы, оставив фоновые

	// с каждым вызовом func consciousnessElementary начинать новый главный цикл мышления
	mainС := resetMineCycleAndBeginAsNew()
	if mainС != nil {
		/* нужно чтобы сразу появилось detectedActiveLastProblemNodID для работы с эпиз.памятью на первых двух уровнях
		В infoFunc8 происходит активация дерева понимания проблемы
		*/
		infoFunc8(mainС)
	}

	if stimulCount > 1 { //не было моторного ответа на прошлый стимул, а уже последовавл новый
		isConfusion = true
		//conscienceStatus+="Произошел конфуз: не было моторного ответа на прошлый стимул, а уже последовавл новый<br>"
	}

	// начать новую последовательность ID инфо-функций, вызываемых после очередной активации
	// НЕТ! infoFuncSequence=nil

	if !only3ltvelMind { //проверка только третьего уровня, без вмешательства первых Включается наверху!

		/*Образ, не имеющий модели понимания, вызывает беспокойство и активирует пассивный режим – исследовать признаки образа, найти ассоциации к ним в личном опыте.
		Это определяется при каждом стимуле.
		*/
		CurrentInformationEnvironment.IsUnknownActionsImageID = 0 // сбросить внимание к прежнему такому образу
		if curActiveActions != nil {
			if isUnknownActionsImage(curActiveActions.ID) { //знаком ли стимул для данных условий? Новизна.
				CurrentInformationEnvironment.IsUnknownActionsImageID = curActiveActions.ID
			}
		}
		//////////////////////////////  ПЕРВЫЙ УРОВЕНЬ  /////////////////////////////////////////////
		/* если не первом уровне автоматизм запускается, то return false
		если не запускается то проблема передается на второй уровень,
		т.е. return true не должно быть на 1-м уровне, кроме случаев:
		1) если запускается альтернативный штатному автоматизм и нужно заблокировать штатный,
		2) были использованы Правила, но бесрезультатно и нет смысла снова пробовать на втором уровне.
		*/

		if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {
			// только если важная ситуация нужно беречь вычислительные ресурсы
			isDreamInterrupt = true // прерывание процесса размышления с новой актуальной проблемой после стимула.
		}

		conscienceStatus += "Отработка первого уровня осмысления<br>"
		motorActionEffect = 0
		mentalInfoStruct.prognoseEffect = 0

		/// экстремально важные объекты внимания для мышления о них
		if detectedActiveLastProblemNodID > 0 { //без detectedActiveLastProblemNodID невозможно искать getExtremImportanceObject()
			getExtremImportanceObject() // найти extremImportanceObject
		}

		//  1 и 2 уровни - только для обработки рвущегося на выполнение автоматизма

		/*!!Не всегда currentAutomatizmAfterTreeActivatedID определяеется ДО understandingSituation(1)
		поэтому в func consciousnessElementary есть свой atmtzmActualTreeNodeID := getAutomatizmFromNodeID(detectedActiveLastNodID)
		и тогда atmtzmActualTreeNodeID оказывается из предыдущей активации дерева автоматизмов.
		*/
		atmtzmActualTreeNodeID = 0 // atmtzmActualTreeNodeID определяется только здесь!
		if wasCurrentAutomatizmAfterTree > 0 && currentAutomatizmAfterTreeActivatedID > 0 {

			atmtzmActualTreeNodeID = currentAutomatizmAfterTreeActivatedID
			// это значит, что 	atmtzmActualTreeNodeID - из старой активации, не требует осмысления как актуальный рвущийся автоматизм

			/*распознать: не навязывают ли стимулом то, что не соотвествует текущей Теме и Цели?
			Меняет состояние CurrentInformationEnvironment.isStimulToForce:
			true - текущий стмул навязывает то, что не соотвествует текущей Теме и Цели
			*/
			recognizerStimulToForce()
		} else {
			atmtzmActualTreeNodeID = 0 // если не нашелся автоматизм на стимул, нужно обнулить ID предыдущего активного автоматизма, иначе он полезет как рвушийся на исполнение и не даст запустить infoFunc()13
			//wasCurrentAutomatizmAfterTree=0// на всякий случай
			//atmtzmActualTreeNodeID = getAutomatizmFromNodeID(detectedActiveLastNodID)
		}
		////////////////////////////////////

		if isIgnoreAutomatizmID(atmtzmActualTreeNodeID) {
			atmtzmActualTreeNodeID = 0
			conscienceStatus += "Игнорирующий штатный автоматизм - расценивается как отсуствие реакции.<br>"
		}

		if atmtzmActualTreeNodeID == 0 {
			// атасная ситуация
			if (EvolushnStage == 4 || CurrentInformationEnvironment.veryActualSituation) && CurrentInformationEnvironment.danger {
				// срочно, не думая, найти аварийный автоматизм, хоть что-то из привязанных к ветке или новый привязать, сделать штатным
				atmtzmActualTreeNodeID = findAlternativeAtmtzm()
			}
		}

		if atmtzmActualTreeNodeID > 0 && CurrentInformationEnvironment.isStimulToForce {
			//текущий стмул навязывает то, что не соотвествует текущей Теме и Цели
			atmtzmActualTreeNodeID = 0
			conscienceStatus += "Автоматизм не соответствует текущей теме и цели.<br>"
			/*позволить пройти дальше чтобы найти альтернативу на 2 уровне ПРАВИЛА
			а если нет, то блокировать всеболее низкоуровневое, перейдя к циклам мышления.
			*/
		}

		// есть ли штатный мот.автоматизм и нужно ли его менять или задумываться
		if atmtzmActualTreeNodeID > 0 { // есть рвущийся на выполнение штатный автоматизм
			//ниже точно такой же вызов - зачем 2 раза одно и тоже?
			//conscienceStatus+="Есть штатный автоматизм <b> <span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(atmtzmActualTreeNodeID) + ")'>" + strconv.Itoa(atmtzmActualTreeNodeID) + "</span>" + "</b><br>"
			mentalInfoStruct.noStaffAtmzmID = false
			mentalInfoStruct.motorAtmzmID = atmtzmActualTreeNodeID // для последующего использования в инфо-фукнциях
			/* Период ожидания ответа LastRunAutomatizmPulsCount при поочередном Стимуле-Ответе есть всегда.
			   А здсь - поиск Ответа именно после каждого Стимула. Так что LastRunAutomatizmPulsCount в функции не учитываем.
			*/
			////////////////////////////// 1 уровень ////////////////////
			// ПЕРВЫЙ УРОВЕНЬ, самый примитивный уровень:

			//НЕТ!!! atmtzmActualTreeNode := GetBelief2AutomatizmListFromTreeId(detectedActiveLastNodID)
			/* учитывается именно тот автоматизм, что рвется на выполнение при активации дерева автоматизмов,
			   даже если он подобран "мягким алгоритмом" в getAutomatizmFromNodeID
			   При атасе он выполняется не раздумявая, иначе подвергается сомнению в infoFunc6()
			*/

			//			atmtzmActualTreeNode = AutomatizmFromId[atmtzmActualTreeNodeID]
			atmtzmActualTreeNode, ok := ReadeAutomatizmFromId(atmtzmActualTreeNodeID)
			if ok && atmtzmActualTreeNode != nil { // есть незаблокированный автоматизм
				if atmtzmActualTreeNode.Usefulness >= 0 { //незаблокированный, м.б. нештатным!

					//	isDangerousModelAutomatism(atmtzmActualTreeNode.ActionsImageID) // проверка функции

					conscienceStatus += "Есть моторный автоматизм <b><span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(atmtzmActualTreeNodeID) + ")'>" + strconv.Itoa(atmtzmActualTreeNodeID) + "</span>" + "</b><br>"
					// Если период сомнений или важная ситуация и нет опасности, то - ПОДВЕРГНУТЬ СОМНЕНИЮ автоматизм.
					if (EvolushnStage == 4 || CurrentInformationEnvironment.veryActualSituation) &&
						!CurrentInformationEnvironment.danger {

						//ПОДВЕРГНУТЬ СОМНЕНИЮ автоматизм, на время размышления заблокировать выплнение штатного
						/* Здесь можно, в зависимости от наработанной инфо-базы, подставлять более подходящий автоматизм, если он
						обнаруживается и привязать его к дереву.
						При этом другие позитивные автоматизмы дерева не блокируются.  В бессознанке выбирается штатный или самый привычный из них.
						Использотьва модели понимания и правила, м.б. значимости для этого в checkAutomatizm(atmtzmActualTreeNode)
						*/
						conscienceStatus += "Подвергнуть сомнению автоматизм в спокойной ситуации.<br>"
						//infoFunc6(cycle) // если все в порядке, он в infoFunc6() запустится, если запущен альтарнативный, то он стал штатным,
						// func checkAutomatizm - бездумная версия проверки
						resA := checkAutomatizm(atmtzmActualTreeNode)
						if resA == nil {
							mentalInfoStruct.noStaffAtmzmID = true
							mentalInfoStruct.motorAtmzmID = 0 // чтобы в usualThinkProcess/infoFunc8 не вернуло true, иначе в usualThinkProcess не дойдет до infoFunc2, а в самой infoFunc2 не дойдет до func13
							atmtzmActualTreeNode = nil        // чтобы сначало отработал поиск в ментальных правилах findSuitableMentalFunc(), а потом уже func13
							//были использованы Правила, но бесрезультатно и нет смысла снова пробовать на втором уровне
							levelOfRunAutomatizm = 1 // для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ
							conscienceStatus += "1 уровень мышления. Автоматизм ID=" + strconv.Itoa(atmtzmActualTreeNodeID) + " подвергнут сомнению и остановлен.<br>"
							isDreamInterrupt = false // прерывание процесса размышления с новой актуальной проблемой после стимула.
							return true              // блокировать старый штатный автоматизм потому, что nil означает опасную ситуацию и отсутствие в правилах положительной перспективы
						}
						if resA.ID == atmtzmActualTreeNodeID { // не изменился выбор
							/* если есть значимая новизна или атас, нужно проверять по правилам прогноз последствий
							   и если для данного действия есть групповое правило, кончающееся негативом, то не выполнять такое действие.
							   Пример:
							      Оператор: «ты кто?»
							      Beast: «бот»
							      Оператор: «уверен?»
							      Beast: «да»
							      Оператор: [Поощрить]
							      Beast: [радуется, смеется, симпатизирует]
							      Оператор: жмет на плашку индикации времени, чтобы прервать диалог и начать новый. Либо ждет, пока индикация сама погаснет.
							      Оператор: «кто ты?»
							      Beast: «Бог»
							      Оператор: «уверен?»
							      Beast: «да»
							      Оператор: [Наказать]
							      Beast: [плачет, прощает]
							      В итоге получатся 2 групповых правила, одно с положительным эффектом, другое с отрицательным.
							   	На Оператор: «кто ты?» нужно прогнозировать отрицательный ответ и не выдавать "бог".
							*/
							//							if EvolushnStage > 4 { //5-я сталия развития.
							// прогноз на основе правил. TODO
							needRulesPrognoz := false
							// при экстремальной отрицательной значимости
							if extremImportanceObject != nil && extremImportanceObject.extremVal < 0 {
								needRulesPrognoz = true
							}
							// в спокойной ситуации
							if !CurrentInformationEnvironment.veryActualSituation && !CurrentInformationEnvironment.danger && !isUnrecognizedPhraseFromAtmtzmTreeActivation {
								needRulesPrognoz = true
							}
							motorActionEffect = atmtzmActualTreeNode.Usefulness
							if needRulesPrognoz {
								// прогноз по данному типу действий автоматизма и правила из последнего кадра эпиз.памяти
								accuracy, effect := getPrognoze(resA)
								mentalInfoStruct.prognoseEffect = effect
								if accuracy > 0 && effect < 0 { //есть негативное предсказание
									//// пусть ищет другой
									motorActionEffect = 0
									mentalInfoStruct.motorAtmzmID = 0 // чтобы в usualThinkProcess/infoFunc8 не вернуло true, иначе в usualThinkProcess не дойдет до infoFunc2, а в самой infoFunc2 не дойдет до func13
									//return true // запретить штатный	НЕТ пусть пройдет до конца функции

									// понизить такой автоматизм, но правило не удалять!
									passivationAutomatizm(resA, -1)
									/* TODO нужно как-то учитывать в infoFunc2 mentalInfoStruct.prognoseEffect, особенно отрицательный.
									т.е. нужно учитывать прогноз (как информированность об эффекте) в соотвествующих инфо-функциях и не только
									*/
									conscienceStatus += "1 уровень мышления. Автоматизм ID=" + strconv.Itoa(resA.ID) + " есть негативный прогноз.<br>"
								}
							}

							//							}
							////////////////

							//motorActionEffect = atmtzmActualTreeNode.Usefulness
							if mentalInfoStruct.prognoseEffect >= 0 && !isUnrecognizedPhraseFromAtmtzmTreeActivation {
								return false // запустить штатный автоматизм
							} else {
								CurrentInformationEnvironment.needThinkingAboutAutomatizm = true
							}
						}
						if resA.ID != atmtzmActualTreeNodeID { // был выбран альтернативный из привязанных к ветке
							runConsciousnessAutomatizm(resA)
							levelOfRunAutomatizm = 1 // для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ
							conscienceStatus += "1 уровень мышления. Автоматизм ID=" + strconv.Itoa(atmtzmActualTreeNodeID) + " остановлен и запущен альтернативный ID=" + strconv.Itoa(resA.ID) + ".<br>"
							isDreamInterrupt = false // прерывание процесса размышления с новой актуальной проблемой после стимула.
							return true              // блокировать старый штатный автоматизм т.к. запущен альтернативный.
						}

						// если есть extremImportanceObject - посмотреть автоматизмы у extremImportanceObject как категории
						/*  старая поддержка категорий убрана, можно посмотреть в categorization.go и categorization_functions.go
						в архиве 2024_01_14_try_episodic_tree_rekease_FINAL_OF_3_VERSION
						ВНИМАНИЕ! любые взаимосвязи теперь находятся по дереву эпиз.памяти.
						т.к. поддержка категорий пока еще не применена нигде,
						то ОСТАВЛЯЕТСЯ НА БУДУЩЕЕ при необходимости.
						Тогда и будут написаны все необходимые функции.
						Пока что удаляется набор категорий и запись файла /memory_psy/category_model.txt
						который не нужен т.к. поиск по дереву эпиз.памяти достаточно быстрый.

												if extremImportanceObject!=nil{
													// выбрать лучший автоматизм из массива ActionsImage категорий экстремального объекта внимания
													res:=getBestAtmtzmFormCategoryExtremImportanceObject()
													if res{ // найден и запущен автоматизм
														return true
													}
												}
						*/

						/*попробовать найти подходящее в дочках дерева (категории - более ранний узел)
						  Категории по фразам уже смотрили выше:
						  alternatives:=getPhraseCategoryChildrens(wordSensor.CurretWordsIDarr)

						   посмотреть категории - все дочки более общего уровня - ФАКТИЧЕСКИ СМОТРИМ БУКВУ АЛФАВИТА...

						    старая поддержка категорий убрана, можно посмотреть в categorization.go и categorization_functions.go
						    в архиве 2024_01_14_try_episodic_tree_rekease_FINAL_OF_3_VERSION
						    ВНИМАНИЕ! любые взаимосвязи теперь находятся по дереву эпиз.памяти.
						    т.к. поддержка категорий пока еще не применена нигде,
						    то ОСТАВЛЯЕТСЯ НА БУДУЩЕЕ при необходимости.
						    Тогда и будут написаны все необходимые функции.
						    Пока что удаляется набор категорий и запись файла /memory_psy/category_model.txt
						    который не нужен т.к. поиск по дереву эпиз.памяти достаточно быстрый.


						  	alternatives := getStimulCategoryChildrens(atmtzmActualTreeNode.BranchID)
						  	if alternatives != nil {
						  		// просмотреть дочки категории и если есть такие экстремальные объекты с позитивом, сделать действие по наилучшему

						  		res := reseachAtmtzmFromStimulCategory(alternatives)

						  		if res { // найден и запущен автоматизм
						  			return true
						  		}
						  	}
						*/
					} else { // при опасности некогда думать, нужно действовать привычно
						conscienceStatus += "Опасная ситуация - запустить штатный автоматизм <b><span style='cursor:pointer;color:blue' onClick='show_automatizms(" + strconv.Itoa(atmtzmActualTreeNodeID) + ")'>" + strconv.Itoa(atmtzmActualTreeNodeID) + "</span>" + "</b>.<br>"
						motorActionEffect = atmtzmActualTreeNode.Usefulness

						// ЗАПИСАТЬ В ДЕРЕВО НОВЫЙ ЭПИЗОД со стимулом и ответом, хотя почему-то было newEpisodeMemory(0,0) без Правила
						/*	saveNewEpisodic(curActions.ID,atmtzmActualTreeNode.ActionsImageID,motorActionEffect) */
						// записать нулевое Правило:
						saveNewEpisodic(curActions.ID, 0, 0, 0)
						isDreamInterrupt = false // прерывание процесса размышления с новой актуальной проблемой после стимула.
						return false             // выполнить страрый штатный автоматизм
					} // если нет - далее искать альтернативу
				} else { // если автоматизм заблокирован
					/*удалить авторитарное Правило с таким действием.
										Иначе никогда не сработает checkForUnbolokingAutomatizm, см. ниже об этом
					НЕЛЬЗЯ УДАЛЯТЬ КАДР ЭПИЗЮПАМЯТИ!!!
					*/
					//conscienceStatus+="Удалить авторитарное Правило с действием заблокированного автоматизма<br>"
					/* НО при создании авторитарного правила (func fixNewRules(lastCommonDiffValue int)) определяется,
										есть ли автоматизм с действием оператора curActiveActionsID, и если у него atmtzmActualTreeNode.Usefulness<0 -
										снять блокировку и сделать штатным (checkForUnbolokingAutomatizm(curActiveActionsID))

					В func searchingRules(trigger int,rImg []int,condType int )(int,int){ делается проверка:
					если действие правила имеется в заблокированном автоматизме, то такое правило исключается и ищутся другие:
					blockExist:=checkBlockedAutomatizm(curR)
					*/

					//Нужно подумать о проблеме автоматизма или проявить инициативу, в общем, запустить func infoFunc25()
					CurrentInformationEnvironment.needThinkingAboutAutomatizm = true

					if EvolushnStage > 4 { // вместо плохого автоматизма - из успешной доминанты
						automatizmStatus = 1
					}
					/*заблокированный автоматизм посылается в RumAutomatizm() не смотря на то, что будет там гарантировано остановлен потому, что так будет виден ответ
					на пульте: автоматизм найден, но заблокирован. При этом mentalInfoStruct.motorAtmzmID = atmtzmActualTreeNodeID позволит потом разобраться с ним с помощью осмысления
					Если заблокировать и сделать переход на 2 уровень, это ничего не даст: там создается автоматизм по учительскому правилу, а не прямому, которое фиксирует существующую
					отработанную реакцию.
					То путь переходит на второй уровень осмысления правил для создания автоматизмов по учительскому правилу и при условии, что нет никакого автоматизма на такой стимул
					Это отзеркаливание старого, не опробованного опыта.*/
					// не позволять рефлексов! return true
				}
			}
			//if currentAutomatizmAfterTreeActivatedID > 0{ // есть рвущийся на выполнение автоматизм
		} else { // нет автоматизма или есть старый автоматизм, например, при игнорировании Ответа оператором
			conscienceStatus += "Нет штатного моторного автоматизма.<br>"
			mentalInfoStruct.noStaffAtmzmID = true
			mentalInfoStruct.motorAtmzmID = 0
			if EvolushnStage > 4 {
				automatizmStatus = 2
			}
			// conscienceStatus+="Нет штатного моторного автоматизма.<br>"

			//if isUnrecognizedPhraseFromAtmtzmTreeActivation { //при активации была нераспознанная фраза
			/* должна быть не только фраза isUnrecognizedPhraseFromAtmtzmTreeActivation,
			а любой стимул, на который нет автоматизма!
			и в таком контексте чтобы сработал диспетчер func infoFunc2
			*/
		}

		/////////////////////////////////////////////////////////////////

		//return false //для тестирования

		//////////////////////////////// 2 уровень ПРАВИЛА ////////////////////////////
		conscienceStatus += "Отработка второго уровня осмысления<br>"
		// не сброшена проблема оценки штатного автоматизма или нет штатного автоматизма
		if mentalInfoStruct.motorAtmzmID > 0 || mentalInfoStruct.noStaffAtmzmID {
			// ВТОРОЙ УРОВЕНЬ - попытка использования примитивных Правил

			/* нужно учитывать, что в правилах действия могут быть типа AmtzmNextString.ID
			- если ID действия превышает по величине prefixActionIdValue
			*/
			var rule Rule // найти Правило
			isFoundRule := false
			/*Найти в расчете на метод GPT последнее известное Правило по цепочке последних limit кадров эпиз.памяти
			  Найти самое свежее Правило, имеющее схожесть с последними кадрами эпизодической памяти
			  для текущей ветки дерева проблем detectedActiveLastUnderstandingNodID, а значит, для текущей совокупности условий.
			  В эпиз.памяти записываются Правила с учетом последовательности образов действий типа AmtzmNextString.ID

			  Последний Стимул при активации Дерева автоматизмов: curActiveActionsID, найти для него наиболее преспективную цепочку Правил,
			  а если нет, то единственное Правило и по нему - рекомендуемое действие.
			*/
			limitN := getLimitCountEM()
			getTargetEpisodicStrIdArr(curActiveActionsID, limitN)
			//выбранная целевая цепочка Правил откуда берем следующее Действие.
			if targetEpisodicStrIdArr != nil {
				/* есть цепочка с конечным плюсовым эффектом, значит можно так действвоать
				а первым членом может быть негативный эффект и если он слишком большой, то не годится
				*/
				rule = targetEpisodicStrIdArr[0]
				if rule.Effect > -3 {
					effect0 := rule.Effect
					if rule.Effect < 0 {
						// есть ли позитив в конце, превышающий негатив
						ruleLast := targetEpisodicStrIdArr[len(targetEpisodicStrIdArr)-1]
						if ruleLast.Effect > effect0 {
							isFoundRule = true
						}
					}
				}
			} else { // не найдено по GPT без учета учительский Правил
				// тогда просто ищем по любым Правилам
				rule = getSingleBestRule(3, curActiveActionsID)
				if rule.Action > 0 {
					isFoundRule = true
				}
			}
			if isFoundRule {
				//по правилу найти или создать (в случае AmtzmNextString) автоматизм и запустить его
				res := makeActionFromRooles(rule) //	!!!!!ПЕРЕДЕЛАТЬ для type Rule struct {
				isDreamInterrupt = false          // прерывание процесса размышления с новой актуальной проблемой после стимула.
				return res
			}

		} //if mentalInfoStruct.motorAtmzmID > 0{ // есть рвущийся на выполнение автоматизм

		// не сброшена проблема оценки штатного автоматизма
		if mentalInfoStruct.motorAtmzmID > 0 { // все еще есть рвущийся на выполнение штатный автоматизм
			conscienceStatus += "Остается проблема оценки штатного автоматизма на втором уровне.<br>"

			//atmtzmActualTreeNode := AutomatizmFromId[mentalInfoStruct.motorAtmzmID]
			atmtzmActualTreeNode, ok := ReadeAutomatizmFromId(mentalInfoStruct.motorAtmzmID)
			// если автоматизм уверенный и ситуация не опасна, то сбросить проблему
			if ok && atmtzmActualTreeNode.Usefulness > 1 && atmtzmActualTreeNode.Count > 3 &&
				CurrentInformationEnvironment.veryActualSituation &&
				!CurrentInformationEnvironment.danger {
				mentalInfoStruct.motorAtmzmID = 0 // пусть выполняется штатный автоматизм
				mentalInfoStruct.noStaffAtmzmID = false
				conscienceStatus += "На втором уровне запущен штатный автоматизм " + strconv.Itoa(mentalInfoStruct.motorAtmzmID) + " т.к. автоматизм уверенный и ситуация не опасна.<br>"
				isDreamInterrupt = false // прерывание процесса размышления с новой актуальной проблемой после стимула.
				return false
			} else {
				conscienceStatus += "Нужно подумать о проблеме оценки штатного автоматизма.<br>"
				// подумать об этом
				if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {
					runNewTheme(12, 5)
				}
				CurrentInformationEnvironment.needThinkingAboutAutomatizm = true
			}
		}
		/////////////////////////////////////////
	} //if !only3ltvelMind{//проверка только третьего уровня, без вмешательства первых Включается наверху!

	// нужно решать проблему в цикле мышления в func consciousnessThinking
	conscienceStatus += "На 1-м и 2-м уровнях осмысления проблема не решена.<br>"
	blockingNewProblemTryCount = false // от быстрого увеличения счетчика проблем в цикле

	// есть ли штатный мент.автоматизм в узле дерева проблем, если есть сравнить полезность
	// ....

	if CurrentInformationEnvironment.isStimulToForce {
		levelOfRunAutomatizm = 2 // для передачи в пульт при ответе бота - на каком уровне осмысления был дан ответ
		conscienceStatus += "2 уровень мышления. Автоматизм ID=" + strconv.Itoa(atmtzmActualTreeNodeID) + " остановлен, так как не соответствует текущей цели.<br>"
		return true // блокировать всеболее низкоуровневое.
	}

	if currentAutomatizmAfterTreeActivatedID > 0 {
		// если это плохой автоматизм, или плохой прогноз в правилах по нему, то не запускать
		a, ok := ReadeAutomatizmFromId(currentAutomatizmAfterTreeActivatedID)
		if !ok || a.Usefulness < 0 || mentalInfoStruct.prognoseEffect < 0 {
			currentAutomatizmAfterTreeActivatedID = 0 // не запускать автоматизм, но разрешить рефлексы
		}
	}

	isDreamInterrupt = false // прерывание процесса размышления с новой актуальной проблемой после стимула.
	return false             // не блокировать последующий код ориентировочного рефлекса.
}

///////////////////////////////
/* сразу после первого прохода consciousnessElementary() запускается певый цикл
позволяющий оценить ситуацию и совершить действия даже без Стимула.
*/
func beginMentalCycle() {
	createNewCycleIteration()
}

////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////
/* Постоянная циркуляция циклов мышления, по каждому пульсу запуск dispetchConsciousnessThinking()
запуск имеющихся циклов по очереди, начиная с главного по каждому пульсу
Неглавные циклы запускаются раз в три пульса?
*/
var curMainCyckle *cycleInfo // текущий главный цикл
func dispetchConsciousnessThinking() {
	if EvolushnStage < 4 {
		return
	}
	if len(cyclesArr) == 0 {
		return
	}
	// сортировать по убыванию
	var mainID = 0
	keys := make([]int, 0, len(cyclesArr))

	noAnyCycle := true
	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		noAnyCycle = false
		if v.isMainCycle {
			curMainCyckle = v
			mainID = id
		} else {
			keys = append(keys, id)
		}
	}

	if noAnyCycle { // все циклы оказались погашены, нужно запустить главный
		cyclesArr = nil    // убираем следы погашенных циклов
		beginMentalCycle() // начать первый, главный цикл мышлления с вызовом func infoFunc8()
		return
	}

	//	lib.MapFree(MapGwardCyclesArr)
	//sort.Ints(keys)
	// отсортировать по убыванию
	sort.Slice(keys, func(i, j int) bool {
		return keys[i] > keys[j]
	})

	// запускать циклы по очереди, начиная с главного
	if mainID > 0 {
		cycle, ok := ReadecyclesArr(mainID)
		if !ok || cycle == nil {
			return
		}
		consciousnessThinking(mainID, cycle)
		cycle.count++
	}
	for _, id := range keys {
		//Неглавные циклы запускаются раз в три пульса?
		if PulsCount%3 == 0 {
			cycle, ok := ReadecyclesArr(id)
			if !ok || cycle == nil {
				return
			}
			consciousnessThinking(id, cycle)
			cycle.count++
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////

var curThinkingPulsCount = 0 // только для контроля
////////////////////////////////////////////////////////////////////////////////////////
/* ментальная часть consciousnessElementary - мышление: 3,4 уровни мышления

Сначала выясняется, стоит ли решать проблему и если да, то
если есть текущая проблема (нет автоматизма) то выбирается цель infoFunc8()
если нет проблемы с автоматизмом и позволяет атас, то смотрятся доминанты.
*/
func consciousnessThinking(cID int, cycle *cycleInfo) {

	if IsSleepingDream { // нужно запускать сновидение.
		/*Сон начинается с перевода главного цикла мышления в пассивный режим (сновидения)
		Если есть инсайт, то просырание и все заново,
		если нет инсайта, начинать по очереди фоновые циклы переводить в главные в пассивном режиме.
		Это - на уровне не func consciousnessElementary(), а в func dispetchConsciousnessThinking()->func consciousnessThinking

		Тут будут проходить все активные циклы мышления, но снивидение всегда будет для главного.
		*/
		if !cycle.isMainCycle {
			// перейти в главный
			cycle = curMainCyckle
			//начать сновидение
			GotoDreaming(cycle) // после исчерпания сюжетя сновижения цикл будет закрыт и установится новый главный.
		}

		return
	}

	if EvolushnStage < 4 {
		return
	}

	//	detectedActiveLastProblemNodID=0// может и не быть проблемы (автоматизм запущен или лень) и тогда пусть остается старая.

	// пустые циклы запускать в 5 раз реже Главнй не может замедляться.
	if !cycle.isMainCycle && cycle.idle {
		if PulsCount%5 != 0 {
			return
		}
	}

	// ступорный цикл не запускать
	if cycle.isStupor {
		return
	}

	if curThinkingPulsCount != PulsCount { // только для контроля

		curThinkingPulsCount = PulsCount
	}
	//////////////////////////////////////
	if correctRepeatCycleFuncStr(cycle) {
		/* предыдущий фрагмент итерации повторяется, ЧТО С ЭТИМ ДЕЛАТЬ?
		в природе в голове часто крутятся назойливые фрагменты...
		Уменьшить скорость вызовов.
		*/
		if PulsCount%5 != 0 {
			return
		}
	}
	cycle.func0Arr = nil        // сначала лог вызовов в течении 1 итерации func consciousnessThinking()
	updateCycleLogsFiles(cycle) // сохранить лог в файле

	//////////////////////////////////////////////////////////////////////

	/// ЭТО - ГЛАВНЫЙ ЦИКЛ - обновлять инфо-среду с каждым проходом
	if cycle.isMainCycle {
		refreshCurrentInformationEnvironment()
		if !IsSleeping { // - без сна
			//ТОЛЬКО ДЛЯ ГЛАВНОГО ЦИКЛА - период ожидания ответа с пульта на действие
			cycle.isWaitingPeriod = CurrentInformationEnvironment.IsWaitingPeriod
		}
	}
	//////////////////////////////////////////

	//////////////////////////////////////////
	if cycle.dreaming {
		//если было прерывание, то восстановить инфу о прерванном цикле InterruptImage
		if restoreDreamingProcess(cycle) {
			cycle.idle = false
			return
		}
	}

	// ПАССИВНЫЙ РЕЖИМ РАЗМЫШЛЕНИЯ
	if (PulsCount - curActiveActionsPulsCount) > waitingTimeForDreamRejim { // прошло > waitingTimeForDreamRejim пульсов со времени последнего стимула
		/* при эмоции с Базовыми контекстами Поиск, Игра, Гон, Агрессия, Страх
		начать размышлеие
		*/
		if !cycle.dreaming {
			if existsBaseContext(2) ||
				existsBaseContext(3) ||
				existsBaseContext(4) ||
				existsBaseContext(8) ||
				existsBaseContext(9) {

				GotoDreaming(cycle) // начать процесс ПАССИВНЫЙ РЕЖИМ РАЗМЫШЛЕНИЯ
				//процесс далее уже не идет
				return
			}
		}
		// блокировка запуска пассивного размышлнения до следующего стимула, который установит curActiveActionsPulsCount=PulsCount
		curActiveActionsPulsCount = 10000000000
	}

	/////////////////////////////////////////////////////////////

	//////////////////////  ЛЕНЬ? (осоловелость)
	/* детекция первичного ленивого состояния
	isIdleness() Учитывает Доминанту.
	*/
	idleness := isIdleness()

	//ТЕСТИРОВАНИЕ только третьего уровня, без вмешательства первых Включается наверху!
	if only3ltvelMind {
		idleness = false
		automatizmStatus = 2 // нет автоматизма
		mentalInfoStruct.motorAtmzmID = 0
		CurrentInformationEnvironment.needThinkingAboutAutomatizm = true // думать как сделать действие
	}

	if idleness { // ЛЕНЬ
		lookForUnactualDominant() // просмотрт доминант для закрытия неактуальных
		if lenessProcess(cycle) {
			cycle.idle = false
			return
		}
	} //if isIdleness()
	////////////////////////////////////

	/////////////////////////  НЕТ ЛЕНИ
	if mentalInfoStruct.ExtremObjID > 0 { // после возврата прерванного в func rememberInterruptImage
		extremImportanceObject = getExtremObjFromID(mentalInfoStruct.ExtremObjID)
		mentalInfoStruct.ExtremObjID = 0
	}

	if extremImportanceObject != nil {
		cycle.impObjID = extremImportanceObject.objID
		weight := lib.Abs(extremImportanceObject.extremVal)
		if weight > cycle.weight {
			cycle.weight = weight
		}
	}
	//////////////////////////////////////////

	/////////////////////////////// для нового цикла:
	if cycle.count == 0 {
		// освободить запрет на запуск автоматизмов.
		wasRunPurposeActionFunc = false

		// cycle.log+="Отработка третьего уровня осмысления  "+strconv.Itoa(cycle.ID)+" мышления с ID циикла "+onClickStr(cycle.ID,"show_cyckle","")+"<br>"
		/* Ментальное определение ближайшей Цели без текущей темы - УЖЕ БЫЛО В НАЧАЛЕ: resetMineCycleAndBeginAsNew()
		   и Активация дерева понимания проблемы ProblemTreeActivation().
		   Если досюда не дошло detectedActiveLastProblemNodID пусть остается старая проблема

		if cycle.isMainCycle { // только для главного цикла (не перезапускать для всех циклов!)
			// только в начале цикла 1 раз
			//if cycle.funcArr!=nil && !lib.ExistsValInArr(cycle.funcArr, 8) {
			// только здеь запускается infoFunc8 !
			 infoFunc8(cycle)
			//}
		}*/

		if isUnrecognizedPhraseFromAtmtzmTreeActivation { //при активации была нераспознанная фраза
			cycle.isWaitingPeriod = false
			if infoFunc2(cycle) {
				return
			}
		}

		if automatizmStatus == 1 { //если автоматизм заблокирован
			// запускать решение доминанты в подходящих условиях т.е. взять опыт из ранее решенных проблм
			res := runDominantaAction(cycle, true) //если подходящий автоматизм найден в успешной Доминанте то он будет запущен
			if res {                               // найден и запущен
				return
			}
		}
		if automatizmStatus == 2 { //нет автоматизма или есть старый автоматизм, например, при игнорировании Ответа оператором
			// запускать решение доминанты в подходящих условиях т.е. взять опыт из ранее решенных проблм
			// не только закрытая доминанта, но и точная по условиям, но не проверенная.
			res := runDominantaAction(cycle, false) //если подходящий автоматизм найден в успешной Доминанте то он будет запущен
			if res {                                // найден и запущен
				return
			}
		}
		if automatizmStatus > 0 { // проблема автоматизма не решена
			//automatizmStatus = 0
			if infoFunc2(cycle) {
				return
			}
			//return
		}
	}

	/////////////////////////////////////////////////////////////

	//////////////////////////////////////////////
	// только если ВАЖНАЯ ИЛИ ОПАСНАЯ СИТУАЦИЯ  не в дреме Блок повышенной акутальности
	if motorActionEffect == 0 && !cycle.dreaming && (CurrentInformationEnvironment.veryActualSituation ||
		CurrentInformationEnvironment.danger) {
		//счетчик нерешенных проблем и открытие доминанты createDominanta(cycle)
		if dangerActualProcess(cycle) {
			cycle.idle = false
			return
		}
	}
	///////////////////////////////////////////////////////////

	//////////////////////// ВАЖНЫЙ ОБЪЕКТ ВНИМАНИЯ
	if extremImportanceObject != nil { // подумать об этом и только об этом
		if EvolushnStage > 4 {
			getCurrentDominant() // по текущему extremImportanceObject
		}
		/* выявляется проблемный объект с отрицательным эффектом - основа мук творчества
		problemExtremImportanceObject = extremImportanceObject
		и делаются первые шаги решения
		*/
		if extremImportanceObjectProcess(cycle) {
			cycle.idle = false
			return
		}
	}
	/////////////////////////////////////////////////////////////

	////////////
	/* ОБЫЧНАЯ СИТУАЦИЯ, О ЧЕМ ДУМАТЬ продолжение шага мышления
	уже запускалось infoFunc8 выше

	if usualThinkProcess(cycle) {
		cycle.idle = false
		return
	}*/
	/////////////////////////////////////////////////////////////

	// чтобы срабатывало не только в func infoFunc2 - неудержимый порыв общения с провокацией
	if EvolushnStage > 3 && mentalInfoStruct.noOperatorStimul {
		infoFunc31pulsCount = 0       // чтобы снова проверить условие isNeedForCommunication()
		if isNeedForCommunication() { // нужно провоцировать оператора
			infoFunc31(cycle)
			return
		}

	}

	//////////////////////////////// мышление о Доминанте
	if EvolushnStage > 4 { //5-я сталия развития. Творчество.
		if dominantsProcess(cycle) { // TODO еще не закончена ф-ция!

			// подавление мешающих стимулов при серьезном поиске решений проблемы
			repressionStimulsNoises() // устанавливает isRepressionStimulsNoise=true

			return
		}
	} //if EvolushnStage > 4
	/////////////////////////////////////////////////////////

	if cycle.idle == false {
		cycle.log += "Цикл - в ХОЛОСТОМ ожидании.<br>"

		//Не было действий Beast  более 100 пульсов, возможно есть условия проявить инициативу
		if CurrentInformationEnvironment.IsIdleness100pulse {
			infoFunc25(cycle) //Посмотреть условия чтобы проявить инициативу
		}
	}
	cycle.idle = true
}

///////////////////////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_cycle.go
____________________________________________
/* Создание, останов, перезапуск ЦИКЛОВ МЫШЛЕНИЯ

 */

package psychic

import (
	"strconv"
)

///////////////////////////////////////////////////////////
/* ID текущего главного циклаfunctionsInAllCickles
НЕ ОБНУЛЯЕТСЯ, а только перекрывается, сохраняя инфу о последнем.
*/
var activedCyckleID = 0 //

// ID запускаемых во всех циклах инфо-функций
var functionsInAllCickles []int

//////////////////////////////////////////////

/////////////////////// ИНФОРМАЦИЯ О ЦИКЛАХ
/* Каждый вновь запускаемый цикл становится главный осознаваемым, а другие уходят в подсознание.

Поддержка многоцикленности мышления:
*/
type cycleInfo struct {
	ID              int    // ID первого звена цикла. Из любого ID шага получается - id:=getBaseIdCyckle(stepID)
	order           int    // порядок нумерации циклов при возникновении нового в createNewCycleIteration
	isMainCycle     bool   // это - главный, осознаваемый цикл мышления
	count           int    // число проделанных шагов
	isWaitingPeriod bool   // это - период ожидания ответа с пульта на действие ТОЛЬКО ДЛЯ ГЛАВНОГО ЦИКЛА
	weight          int    // значимость цикла в конкуренции параллельных
	pulsCount       int    // старые циклы и взаимная конкурентность циклов - уменьшает их число
	themeType       int    // тема мышления в момент создания цикла, ThemeImage.Type
	problevID       int    // проблема в момент создания цикла
	impObjID        int    // последний extremImportanceObject.extremObjID (перекрывается новым)
	lastFuncID      int    // какая последняя инфо-функция вызывалась в этом цикле - против дребезга
	lastProcessID   string //название последней из функций процесса в understanding_functions.go вызывалась
	func0Arr        []int  // последовательность вызываемых функций этого цикла в течении одной итерации  func consciousnessThinking()
	funcArr         []int  // последовательность вызываемых функций этого цикла
	idle            bool   // пустой цикл (крутит вхолостую) - запускаются в 5 раз реже: PulsCount%5 !=0
	isStupor        bool   // этот цикл, пришедший в ступор, больше не перезепускать
	/* Фоновые циклы могут продолжать обрабатывать инфу в режиме dreaming, но если это происходит в главном цикле,
	   то это значит общий режим пассивного размышления, который тормозит восприятие стимулов (isRepressionStimulsNoise).
	*/
	dreaming bool // пассивный режим размышления (сновидение или мечтания)
	/*	ID кадра истории эпиз.памяти в сновидении или мечтании при процессе текущего прохода,
		после чего isDreamInterrupt устанавливается в значение для продолжения прохода.
		При isDreamInterrupt==true процесс приостанавливается и потом продолжается с текущего dreamingEpisodeHistoryID
	*/
	dreamingEpisodeHistoryID int // текущий ID исторической памяти EpisodicHistoryArr[] при процессе текущего прохода

	log string // последовательность шагов для пульта
}

// инфа о работающих параллельно ментальных циклах - глобальный детектор занятости
// var cyclesArr =make(map[int]*cycleInfo)
var cyclesArr []*cycleInfo // сам массив
// var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WritecyclesArr(index int, value *cycleInfo) {
	addcyclesArr(index)
	cyclesArr[index] = value
}
func addcyclesArr(index int) {
	if index >= len(cyclesArr) {
		newSlice := make([]*cycleInfo, index+1)
		copy(newSlice, cyclesArr)
		cyclesArr = newSlice
	}
}

// считывание члена
func ReadecyclesArr(index int) (*cycleInfo, bool) {
	if index >= len(cyclesArr) || cyclesArr[index] == nil {
		return nil, false
	}
	return cyclesArr[index], true
}

////////////////////////////////////

// усли превысит 1000, то в func sleepNecessityDetector() - потребность во сне
func GetCycleCount() int {
	return len(cyclesArr)
}

///////////////////////////////////

/* создание нового cycleInfo
 */
var countbaseID = 0

func addCounterGoNext() *cycleInfo {
	var new cycleInfo
	countbaseID++
	new.ID = countbaseID
	cycle := &new
	cycle.pulsCount = PulsCount
	cycle.isMainCycle = true // новый - всегда главный
	activedCyckleID = cycle.ID

	//	cyclesArr[countbaseID] = cycle
	WritecyclesArr(countbaseID, cycle)

	return cycle
}

/////////////////////////////////////

// /  установить цикл как главный
func setAsMaimCycle(baseID int) {

	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		if id == baseID {
			v.isMainCycle = true
			activedCyckleID = id
		} else {
			v.isMainCycle = false
			// 			v.dreaming=1 //все не главные циклы делать дремными
			//			НЕТ отличие только в озарении для неглавных
		}
	}

	return
}

///////////////////////////////////

// сбросить все в цикле чтобы начал как новый
func resetCycleAndBeginAsNew(cycle *cycleInfo) {
	cycle.count = 0
	cycle.isWaitingPeriod = false
	cycle.dreaming = false
	cycle.pulsCount = 0
	cycle.themeType = 0
	cycle.problevID = 0
	cycle.impObjID = 0
	cycle.lastFuncID = 0
	cycle.lastProcessID = ""
	cycle.func0Arr = nil
	cycle.funcArr = nil
	cycle.idle = false
	cycle.isStupor = false
}

// ////////////////////// возвращает главный цикл
func resetMineCycleAndBeginAsNew() *cycleInfo {
	if len(cyclesArr) == 0 { // еще нет циклов
		beginMentalCycle() // начать первый, главный цикл мышлления с вызовом func infoFunc8()
	}
	var mainС *cycleInfo
	for _, v := range cyclesArr {
		if v == nil {
			continue
		}
		if v.isMainCycle {
			mainС = v
			resetCycleAndBeginAsNew(v) // сбросить все в цикле чтобы начал как новый
			break
		}
	}
	return mainС
}

/////////////////////////////////////////////////

// найти новый главный цикл по значимости, кроме удаляемого
func foundMainCycle(delID int) {
	// удаленный не является главным?
	//c,ok:=ReadecyclesArr(delID)
	c, ok := ReadecyclesArr(delID)
	if ok && !c.isMainCycle {
		return // значит есть уже главный
	}
	var max = 0
	var idMax = 0

	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		if v.weight > max {
			max = v.weight
			idMax = id
		}
	}

	if idMax > 0 {
		setAsMaimCycle(idMax)
	}
}

///////////////////////////////////////////

func deleteCycle(baseID int) {
	foundMainCycle(baseID) //найти новый главный цикл по значимости, кроме удаляемого

	//	delete(cyclesArr, baseID)
	WritecyclesArr(baseID, nil)
}

/*
	закончить ВСЕ циклы мышления

приведет к прерыванию циклов в func consciousnessElementary: //если нет такого цикла, то завершить эту итерацию
Используется только в func endAllCycles()
*/
func deleteAllCounterGoNext() {
	//cyclesArr =make(map[int]*cycleInfo) // сброс всех счетчиков
	cyclesArr = nil
}

//////////////////////////////////////////

// погасить все циклы кроме дремы
func endNoDereamsCycles() {

	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		if !v.dreaming {
			endBaseIdCycle(id)
		}
	}
}

// погасить все циклы дремы  mine - только главные
func endDereamsCycles(mine bool) {
	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		if mine && !v.isMainCycle {
			continue
		}
		if v.dreaming {
			endBaseIdCycle(id)
		}
	}
}

// все циклы - дремы
func setDereamsForAllCycles() {
	for _, v := range cyclesArr {
		if v == nil {
			continue
		}
		v.dreaming = true
	}
}

// главный цикл вывести из дремы
func setWakuoForMainCycles() {
	for _, v := range cyclesArr {
		if v == nil {
			continue
		}
		if v.isMainCycle {
			v.dreaming = false
		}
	}
}

////////////////////////////////////////////////

/*
	Начать новый цикл мышления. Или начать новый с прерванного fromID.

Создать новое Базовое звено цепи для данной активности деревьев
и пройти цепочку до конца, чтобы продолжить цикл от него.

Создать уникальный ID нового цикла - cycleID (cycleID,goNext:=createNewCycleIteration()) делать НЕ СТОИТ
т.к. тогда придется вызывать func consciousnessElementary с этим параметром

типично:
createNewCycleIteration()// начать новый цикл
*/
var curCycleOrder = 0 // порядок создания цикла, всегда УНИКАЛЬНЫЕ ЗНАЧЕНИЯ даже при удалении промежуточных циклов
func createNewCycleIteration() *cycleInfo {
	shotrMemepMemArr = nil
	extremImportanceObject = nil
	problemExtremImportanceObject = nil
	extremImportanceMentalObject = nil

	// НАЧАТЬ НОВЫЙ ЦИКЛ
	c := addCounterGoNext() // новый цикл - всегда главный
	activedCyckleID = c.ID
	curCycleOrder++
	c.order = curCycleOrder

	node, ok := ReadeThemeImageFromID(oldThemeID)
	if ok {
		c.themeType = node.Type // - от последнего func runNewTheme и там перекроется, если цикл создан в ней.
	}

	c.problevID = detectedActiveLastUnderstandingNodID
	c.weight = getCycleWeight()
	c.log += GetSelfPerceptionInfo() //getCurInformEnviroment()
	c.log += conscienceStatus        // сохранить conscienceStatus объективного прохода

	setAsMaimCycle(c.ID) // сделать главным

	reduseUpdating() //старые циклы и взаимная конкурентность циклов - уменьшает их число

	conscienceStatus = ""
	c.log += "<span style='color:#006300'><b>Начат новый цикл мышления c ID=" + strconv.Itoa(c.ID) + ".</b></span><br>"

	return c
}

//////////////////////////////////////////////////

/*
	прерывание осмысления с сохранением предыдущего и началом нового цикла - итак происходит - новый цикл - главный

возврат к прерванному мышлению rememberInterruptImage()
*/
func interruptMentalWork(cycleID int) {
	if len(cyclesArr) == 0 {
		return
	}
	// прерывание только если важная ситуация
	if !CurrentInformationEnvironment.veryActualSituation && !CurrentInformationEnvironment.danger {
		return
	}
	addInterruptMemory(cycleID)
	idlenessType = 0

	//	wasInterruptedMentCickle = true // начало прохода ментального цикла
	//	functionsInAllCickles = nil     //ID запускаемых в текущем цикле инфо-функций

}

//////////////////////////////////////////////////////
/* выборка прерванного размышления из стека и начало размышления об этом

 */
func rememberInterruptImage(c *cycleInfo) *InterruptImage {
	// вспомнить последнее прерванное размышление
	lastImg := InterruptMemory[len(InterruptMemory)-1]
	mentalInfoStruct.ThemeImageType = lastImg.ThemeImageType
	mentalInfoStruct.mentalPurposeID = lastImg.PurposeImageID

	mentalInfoStruct.ExtremObjID = lastImg.ExtremObjID

	// удалить последний элемент массива
	if len(InterruptMemory) > 0 {
		InterruptMemory = InterruptMemory[:(len(InterruptMemory) - 1)]
	}

	// начать размышление с прерванного шага, не с самого начала
	//запустить прерванное размышление
	//createNewCycleIteration(lastID)// тут обнулялось extremImportanceObject
	setAsMaimCycle(c.ID) ///  просто установить цикл как главный

	if lastImg.ExtremObjID > 0 {
		extremImportanceObject = getExtremObjFromID(lastImg.ExtremObjID)
	}

	return lastImg
}

//////////////////////////////////////////////////////////////////////

// ///////////////////////////////////////
// закончить цикл с базовым ID
func endBaseIdCycle(baseID int) {
	foundMainCycle(baseID) //найти новый главный цикл по значимости, кроме удаляемого

	//Вызывает постоянную блокировку т.к. во внешнем цикле уже есть неснятая блокировка
	//!!!	lib.MapCheck(MapGwardCyclesArr)

	deleteCycle(baseID)
}

/////////////////////////////////////////
/* закончить ВСЕ циклы мышления
приведет к прерыванию циклов в func consciousnessElementary: //если нет такого цикла, то завершить эту итерацию
*/
func endAllCycles() {
	deleteAllCounterGoNext() // удаление всех счетчиков всех циклов -
	stopCyckles()            //  закончить обслуживание циклов мышления
}

// ///////////////////////////////////////
//
//	закончить обслуживание всех циклов мышления
func stopCyckles() {
	functionsInAllCickles = nil //ID запускаемых в текущем цикле инфо-функций

	conscienceStatus += "Остановка всех циклов мышления.<br>"
}

////////////////////////////////////////////

// старые циклы и взаимная конкурентность циклов - уменьшает их число
func reduseUpdating() {
	var delList = "" // список удаленных
	// удалить очень старые
	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		if (PulsCount-v.pulsCount) > 3600 && !v.isMainCycle { // старше часа
			delList += strconv.Itoa(v.order) + ";"

			//delete(cyclesArr, id)
			WritecyclesArr(id, nil)

		}
	}

	// взаимное торможение в каждой группе goNextFromUnderstandingNodeIDArr, оставлять только один самый весомый или главныый цикл из всего пакета
	//разбить по группам
	var themArr = make(map[int][]*cycleInfo)
	/*  не нужно по проблемам, раз есть по темам ?
	var problemArr []int // какие ШВ проблем в циклах
	// сгруппировать по проблемам TODO
		for id, v := range problemArr {
		if v==nil{continue}
			cyclesArrMapCheck()
			base,ok := cyclesArr[v[0].ID]
			if ok{
				themArr[id]=append(themArr[id],base)
			}
		}
		// конкуренция по каждой группе
		for _, v := range themArr {
		if v==nil{continue}
			delList+=cycleLateralBraking(v)
		}
	*/
	/////////////////////////////////////////////// удаление циклов
	// взаимное торможение в каждой группе одинаковых Тем, оставлять только один самый весомый или главныый цикл из всего пакета
	//разбить по темам
	themArr = make(map[int][]*cycleInfo)

	for _, v := range cyclesArr {
		if v == nil {
			continue
		}
		themArr[v.themeType] = append(themArr[v.themeType], v)
	}

	// конкуренция по каждой теме
	for _, v := range themArr {
		if v == nil {
			continue
		}
		delList += cycleLateralBraking(v)
	}

	if len(delList) > 0 {
		conscienceStatus += "<span style='color:red'>" + delList + " - циклы удалены в ходе взаимной конкуренции и устаревания.</span><br>"
	}
}

// / конкуренция в каждом массиве циклов
func cycleLateralBraking(cArr []*cycleInfo) string { // возвращает число удаленных
	if len(cArr) < 2 { // раз один, то нет конкуренции
		return ""
	}
	var delList = ""
	max := 0
	cID := 0
	age := 0 // возраст
	for i := 0; i < len(cArr); i++ {
		v := cArr[i]

		//base,ok := cyclesArr[v.ID]
		base, ok := ReadecyclesArr(v.ID)
		if !ok {
			continue
		}
		if base.isMainCycle {
			cID = v.ID
			break
		}
		if base.weight > max {
			max = base.weight
			age = base.pulsCount
			cID = v.ID
		}
		// если равны по значимости, то преимущество в более молодом возрасте
		if base.weight == max && base.pulsCount < age {
			max = base.weight
			age = base.pulsCount
			cID = v.ID
		}
	}
	if cID > 0 { // удалить все кроме cID
		for i := 0; i < len(cArr); i++ {
			if cArr[i].ID == cID {
				continue
			}
			if !cArr[i].isMainCycle {
				delList += strconv.Itoa(cArr[i].order) + ";"

				//				delete(cyclesArr, cArr[i].ID)
				WritecyclesArr(cArr[i].ID, nil)

			}
		}
	}
	return delList
}

////////////////////////////////////////////////////////////////////////////

/*
	если данный цикл не выполняет никакого действия,

а тупо крутится вхолостую, то замедлить его цркуляцию.
Вызывается в самом конце функции активации осознания, значит, прошло без пользы.
*/
func endIdleCycle(cycle *cycleInfo) bool {
	// такое возможно ли?
	/*
		 if functionsInAllCickles == nil{//ID запускаемых в текущем цикле инфо-функций нет
			 conscienceStatus+="<span style='color:red'>Шаг цикла ID="+ strconv.Itoa(cycle.ID)+" прошел без вызова инфо-функций. Цикл закрывается</span><br>"
			 //cycle.needFinish=true
			 endBaseIdCycle(cycle.ID)
			 return true
		 }*/
	// просто тормозим проход активации на 0.5 секунды чтобы с такой скоростью следил за событиями.
	//	time.Sleep(500 * time.Millisecond)//БЕСПОЛЕЗНО ПРИМЕНЯТЬ SLEEP т.к. он блокирует не только данную горутину, но и другие!!!!
	return true
	/*
		 // Уменьшенное числа холостых циклов по сравнению с limitMaxCycleStepCount:
			if cycle.count > limitMaxCycleStepCount/10  {
				conscienceStatus+="<span style='color:red'>Шаг цикла ID="+ strconv.Itoa(cycle.ID)+" прошел в холостую "+ strconv.Itoa(limitMaxCycleStepCount/10)+" раз. Цикл закрывается</span><br>"
		//		cycle.needFinish=true
				endBaseIdCycle(cycle.ID)
				return true
			}
			return false */
}

////////////////////////////////////////////////

/*
	  В момент создания нового цикла (func addCounterGoNext)
			определить его первоначальную значимость.

В ходе цикла значимость может меняться.
*/
func getCycleWeight() int {
	var w = 0
	if CurrentInformationEnvironment.veryActualSituation {
		w += 1
	}
	if CurrentInformationEnvironment.danger {
		w += 1
	}
	if problemExtremImportanceObject != nil {
		w += 1
	}

	return w
}

// ///////////////////////////////////////////////
// инфа для пульта по get_short_info(
func GetCycleLocInfo(cID int) string {
	//	c,ok:= cyclesArr[cID]
	c, ok := ReadecyclesArr(cID)
	if ok {
		return c.log
	}
	return "Нет цикла с ID=" + strconv.Itoa(cID)
}

// ///////////////////////////////////////
// инфа для пульта по function show_cyckle(id)
func GetCycleInfo(id int) string {
	out := ""
	cInfo := ""
	// точно ли есть такой живой цикл
	//	c,ok:= cyclesArr[id]
	c, ok := ReadecyclesArr(id)
	if ok {
		if c.isMainCycle {
			cInfo += "Это - главный, осознаваемый цикл мышления.<br>"
		}
		if c.dreaming {
			cInfo += "Режим пассивного размышления.<br>"
		}

		cInfo += "Значимость цикла: " + strconv.Itoa(c.weight) + "<br>"
		cInfo += "Тема мышления: " + strconv.Itoa(c.themeType) + "<br>"
		cInfo += "<span style='color:blue;cursor:pointer;' onClick='get_short_info(" + strconv.Itoa(c.ID) + ")'>Последовательность шагов</span><br>"
	} else {
		return "Нет такого Цикла мышления."
	}
	out = "Цикл мышления " + strconv.Itoa(c.count) + " шагов<br>"
	out += cInfo

	return out
}

////////////////////////////////////////////

// инфа о цикле
func GetCycleStrInfo(cID int) string {
	out := "Нет цикла с ID = " + strconv.Itoa(cID) + "<br>"
	//	cycle,ok:= cyclesArr[cID]
	cycle, ok := ReadecyclesArr(cID)
	if ok {
		out = "Цикл ID = " + strconv.Itoa(cID) + "<br>"
		if cycle.isMainCycle {
			out += "Это - главный цикл.<br>"
		}
		out += "Число звеньев цепочки: " + strconv.Itoa(cycle.count) + "<br>"
		out += "Значимость цикла: " + strconv.Itoa(cycle.weight) + "<br>"
		if cycle.dreaming {
			out += "Это цикл пассивного размышления.<br>"
		}
	}
	return out
}

//////////////////////////////////////////////////////

// в главном цикле - режим пассивного размышления
func IsDreamMainProcess() *cycleInfo {
	for _, v := range cyclesArr {
		if v == nil {
			continue
		}
		if v.dreaming {
			return v
		}
	}
	return nil
}

///////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_for_pult.go
____________________________________________
/* показывать состояние психики на Пульте

 */

package psychic

import (
	"BOT/lib"
	"sort"
	"strconv"
)

/////////////////////////////////////////////

// выдать текущую инфу для http://go/pages/mental_cicles.php
/*
func GetCicklesToPult()(string){


	style:="style='font-size:19px;font-weight:bold;cursor:pointer'"
	//	Текущая ситуация:
	out:="<b>Текущая ситуация:</b><br>"
		out+="<table cellpadding=0 cellspacing=0 border=1 class='main_table'>"
		out+="<tr>"
		out+="<th class='table_header'>ID дерева понимания ситуации</th>"
		out+="<th class='table_header'>ID дерева автоматизмов</th>"
		out+="</tr>"

		out+="<tr>"
		out+="<td class='table_cell'><span "+style+" onClick='show_unde_tree("+strconv.Itoa(detectedActiveLastUnderstandingNodID)+")'>"+strconv.Itoa(detectedActiveLastUnderstandingNodID)+
		"</span></td><td class='table_cell'><span "+style+" onClick='show_atmzm_tree("+strconv.Itoa(detectedActiveLastNodID)+")'>"+strconv.Itoa(detectedActiveLastNodID)+
		"</span></td></tr>"
		out+="</table>"
/////////////  прерывания ращмышлений
if InterruptMemory!=nil && len(InterruptMemory)>0{
	out="<b>Прерывания размышлений:</b><br>"
	out+="<table cellpadding=0 cellspacing=0 border=1 class='main_table'>"
	out+="<tr>"
	out+="<th class='table_header'>ID итерации</th>"
	out+="<th class='table_header'>Тема размышления</th>"
	out+="<th class='table_header'>ID поставленной цели</th>"
	out+="</tr>"
	for i := 0; i < len(InterruptMemory); i++ {
		im := InterruptMemory[i]
		theme:=ThemeImageFromID[mentalInfoStruct.ThemeImageType]
	out+= "<tr><td class='table_cell'><span " + style + " )'>" + strconv.Itoa(im.goNextID) +
	"</span></td><td class='table_cell'><span " + style + " >" + ThemeTypeStr[theme.Type] +
	"</span></td><td class='table_cell'><span " + style + " onClick='get_purpose(" + strconv.Itoa(im.PurposeImageID) + ")'>" + strconv.Itoa(im.PurposeImageID) +
	"</span></td></tr>"
	}
	out+="</table><br>"
}

//////////////   циклы осмысления
	cycle:=getCyckleGoNextIdArr(activedCyckleID)
	if cycle==nil || len(cycle)==0{
		out+="Нет текущего цикла осмысления.<hr><br><br>"
	}else {// выдать таблицу всх составляющих цикла

cLen:=len(cycle)
isLimit:=0
if cLen >20{// не больше 20 показывать
	cycle=cycle[cLen:]
	isLimit=cLen-20
}
		out+=showGoNextCykles(cycle,isLimit)
	}

	if idlenessType>0{
		detStr:="Нет осознанной цели и проблемы"
		if idlenessType==1{
			detStr="Благополучное состояние (осоловелость)"
		}
		out+="<br><span style='style='font-size:19px;'>Нет мышления  - <b>ЛЕНИВОЕ СОСТОЯНИЕ:</b> "+
			detStr+"</span><br>"
	}


	// последнгие 20 кадров кратковременной памяти
	//	termMemory=[]shortTermMemory{{4800,5,1},{7188,4,2},{4800,3,3}} // тестирование
	if termMemory == nil || len(termMemory)==0{
		out+="Еще нет кратковременной памяти."
	}else {
	var termMemoryFrag []shortTermMemory
	if len(termMemory)>20{
		termMemoryFrag = termMemory[:20]
	}else{
		termMemoryFrag=termMemory
	}
	out+="<br><b>Кратковременная память (последние 20 кадров):</b><br>"

		out += "<hr><table cellpadding=0 cellspacing=0 border=1 class='main_table'>"
		out += "<tr><th class='table_header'>goNext ID</th>"
		out += "<th class='table_header'>ID дерева понимания</th>"
		out += "<th class='table_header'>ID дерева автоматизмов</th>"
		out += "</tr>"
		for i := len(termMemoryFrag) - 1; i >= 0; i-- {
			sm := termMemory[i]
			if sm.GoNextID == 0 {
				//return "Нулевой образ звена цикла в GetCicklesToPult()."
				out += "<tr><td colspan=10>Нулевой образ звена цикла в GetCicklesToPult() с ID = " + strconv.Itoa(sm.GoNextID) + "</td></tr>"
			}
			goNextFromIDMapCheck()
			gn := goNextFromID[sm.GoNextID]
			if gn == nil {
				out += "<tr><td colspan=10>Нет образа звена цикла с ID = " + strconv.Itoa(sm.GoNextID) + "</td></tr>"
			}else {
				style := "style='font-size:19px;font-weight:bold;cursor:pointer'"
				out += "<tr><td class='table_cell'><span style='color:#666666'>" + strconv.Itoa(gn.ID) +
					"</span></td><td class='table_cell'><span " + style + " onClick='show_unde_tree(" + strconv.Itoa(sm.uTreeNodID) + ")'>" + strconv.Itoa(sm.uTreeNodID) +
					"</span></td><td class='table_cell'><span " + style + " onClick='show_atmzm_tree(" + strconv.Itoa(gn.MotorBranchID) + ")'>" + strconv.Itoa(gn.MotorBranchID) +
					"</span></td></tr>"
			}
		}
		out += "</table>"
	}
	out:="Сделать показ логов циклов мышления по сохраненным в файлах !!!!!"
	return out
}*/
//////////////////////////////////////////
func showGoNextCykles(goNext []int, isLimit int) string {
	//style:="style='font-size:19px;font-weight:bold;cursor:pointer'"
	out := "<b>Последовательность инфо-функций:</b>"
	out += "<br>"

	for i := 0; i < len(goNext); i++ {
		gn := goNext[i]
		if gn != 0 {
			out += strconv.Itoa(gn) + ": " + getMentalFunctionString(gn)
		}
		if isLimit > 0 {
			out += "...Еще есть " + strconv.Itoa(isLimit) + " функций...<br>"
		}
		out += "<br>"
	}
	return out
}

//для http://go/pages/mental_rules.php инфа о ID goNext
/*
func GetGoNextInfo(id int)(string){
	if id==0{
		return "Нулевой образ звена цикла в GetGoNextInfo."
	}

	goNextFromIDMapCheck()
		gn:= goNextFromID[id]
		if gn==nil{
			return "Нет образа звена цикла с ID = "+strconv.Itoa(id)
		}
		style:="style='font-size:19px;font-weight:bold;cursor:pointer'"
		out:="<tr><td class='table_cell'><span style='color:#666666'>"+strconv.Itoa(id)+
			"</span></td><td class='table_cell'><span "+style+" onClick='show_atmzm_tree("+strconv.Itoa(gn.MotorBranchID)+")'>"+strconv.Itoa(gn.MotorBranchID)+"</span> "+
			"</span></td><td class='table_cell'><span "+style+" onClick='show_ment_act_img("+strconv.Itoa(gn.MentalActionsImagesID)+")'>"+strconv.Itoa(gn.MentalActionsImagesID)+
			"</span></td></tr>"

	return out
}*/
//////////////////////////////////////////////////////////////////
// для GetCicklesToPult() инфа о ветке дерева автоматизмов
func GetAtmzmTreeInfo(id int) string {
	out := ""

	//node:=AutomatizmTreeFromID[id]
	node, ok := ReadeAutomatizmTreeFromID(id)
	if !ok {
		return "Нет такого узла дерева автоматизмов."
	}
	out += getStrFromCond(0, node.BaseID) + "<br>"
	out += getStrFromCond(1, node.EmotionID) + "<br>"
	out += getStrFromCond(2, node.ActivityID) + "<br>"
	out += getStrFromCond(3, node.ToneMoodID) + "<br>"
	//if node.PhraseID>0 { нафиг масикировать лажи! первый символ не должен быть в ветке, если нет фразы!
	out += getStrFromCond(4, node.SimbolID) + "<br>"
	//}
	out += getStrFromCond(5, node.PhraseID) + "<br>"
	return out
}

// для GetCicklesToPult() инфа о ветке дерева понимания
func GetUndstgTreeInfo(id int) string {
	out := ""
	//	node:=UnderstandingNodeFromID[id]
	node, ok := ReadeUnderstandingNodeFromID(id)
	if !ok {
		return "Нет такого узла дерева понимания."
	}
	out += getMoodStr(node) + "<br>"
	out += getEmotionStr(node) + "<br>"
	out += getSituationStr(node) + "<br>"
	//out += getPurposeString(node.ID) + "<br>" // это дерево ситуации, а не дерево проблем
	return out
}

// для GetCicklesToPult() инфа о ментальном автоматизме
func GetMentAtmzmInfo(id int) string {
	/*out:=""
	//	atmz:=MentalAutomatizmsFromID[id]
		atmz,ok:=ReadeAFromID(id)
		if !ok{
			return "Нет такого узла дерева понимания."
		}
		if atmz.IsStaff==1{
			out += "Это - штатный автоматизм<br>"
		}else{
			out += "Это - НЕ штатный автоматизм<br>"
		}
		out += "Число шагов в цикле goNext: "+strconv.Itoa(len(atmz.funcArr))+"<br>"
		out += "(Бес)Полезность: "+strconv.Itoa(atmz.Usefulness)+"<br>"
		out += "ID запущенного моторного автоматизма: <span onClick='show_automatizms("+strconv.Itoa(atmz.motAutmtzmID)+")' style='cursor:pointer;color:blue'>"+strconv.Itoa(atmz.motAutmtzmID)+"</span><br>"
	*/
	return "Мент.Автоматизм - наиболее привычная цепь инфо-фукций - специфична к кокретным условиям."
}

//////////////////////////////////////////////////////////////////

// выдать текущую инфу Oбъекты значимости для http://go/pages/mental_importance.php
func GetImportanceToPult() string {
	/*
		//	saveFromNextIDcurretCicle=[]int{1,2,3} // тестирвоание
		out := ""
		iArr := importanceFromID
		if iArr == nil || len(iArr) == 0 {
			out += "Еще нет объектов значимости.<hr><br><br>"
		} else {
			out += "<table cellpadding=0 cellspacing=0 border=1 class='main_table'>"
			out += "<tr><th class='table_header'>ID образа<br>значимости</th>"
			out += "<th class='table_header'>ID<br>объекта</th>"
			out += "<th class='table_header'>ID<br>дерева<br>проблем</th></th>"
			out += "<th class='table_header' width=350>Тип объекта</th>"
			out += "<th class='table_header'>Значимость</th></tr>"

			keys := make([]int, 0, len(iArr))

			for k, v := range iArr {
				if v == nil {
					continue
				}
				keys = append(keys, k)
			}

			sort.Ints(keys)

			for _, k := range keys {
				//			oi,ok:=importanceFromID[k]
				oi, ok := ReadeImportanceFromID(k)
				if !ok {
					out += "<tr><td class='table_cell'>Нет объекта с ID=" + strconv.Itoa(k) + "</span>"
					continue
				}

				style := "style='font-size:19px;font-weight:bold;cursor:pointer'"
				out += "<tr><td class='table_cell'><span style='color:#666666'>" + strconv.Itoa(oi.ID) + "</span>" +
					"<td class='table_cell'>" + strconv.Itoa(oi.ObjectID) + "</td>"

				if oi.Type == 0 || oi.Type > 8 {
					out += "<tr><td class='table_cell'>Нет объекта с importanceTypeName =" + strconv.Itoa(oi.Type) + "</span>"
					continue
				}

				out += "<td class='table_cell' " + style + " onClick='show_problem_tree(" + strconv.Itoa(oi.ProblemID) + ")'>" + strconv.Itoa(oi.ProblemID)

				out += "</td><td class='table_cell' onClick='show_object(" + strconv.Itoa(oi.Type) + "," + strconv.Itoa(oi.ObjectID) + ")'>type=" + strconv.Itoa(oi.Type) + ": " +
					"</td><td class='table_cell'>" + strconv.Itoa(oi.Value) +
					"</td></tr>"
			}
		}
		out += "</table>"

		return out
	*/
	return ""
}

// информация об объекте значимости
func GetImportanceObjectInfo(objID int) string {
	out := ""

	out = GetActionsString(objID) +
		"<br><br><span style='cursor:pointer;color:blue' onClick='get_undestand_model(" + strconv.Itoa(objID) + ")'>Модель понимания объекта ID=" + strconv.Itoa(objID) + "</span>"

	return out
}

/////////////////////////////////////////////////

/*
func show_atmzm_tree(actID int)string{
	out:=""
	MentalActionsImagesMapCheck()
	act:=MentalActionsImagesArr[actID]
	switch act.typeID{
	case 1: out="активация настроения Mood в дереве понимания: "+strconv.Itoa(act.typeID)
	case 2: out="активация эмоции "+strconv.Itoa(act.typeID)+" в дереве понимания"
	case 3: out="активация PurposeImage "+strconv.Itoa(act.typeID)+"в дереве понимания"
	case 4: out="запуск инфо-функции infoFunc"+strconv.Itoa(act.typeID)+"()"
	case 5: out="запуск моторного автоматизма ID="+strconv.Itoa(act.typeID)
	case 6: out="запуск Доминанты ID="+strconv.Itoa(act.typeID)
	case 7: out="создание новой Доминанты"
	default: out="<span style=''>НЕТ ТАКОГО МЕНТАЛЬНОГО ДЕЙСТВИЯ</span> c ID="+strconv.Itoa(act.typeID)
	}

	return out
}*/
/////////////////////////////////////////////////

/* вставка строки с вызовом onClick
 */
func onClickStr(id int, onclick string, title string) string {
	out := "<b><span style='cursor:pointer;color:blue' title='" + title + "'"
	out += "onClick='" + onclick + "(" + strconv.Itoa(id) + ")'>" + strconv.Itoa(id) + "</span>" + "</b>"
	return out
}
func onClick2Str(par1 int, par2 int, id int, onclick string, title string) string {
	out := "<b><span style='cursor:pointer;color:blue' title='" + title + "'"
	out += "onClick='" + onclick + "(" + strconv.Itoa(par1) + "," + strconv.Itoa(par2) + ")'>" + strconv.Itoa(id) + "</span>" + "</b>"
	return out
}

///////////////////////////////////////////////////

// Страница Сознание
// var conscienceInfoCount=0
func GetConscienceInfo() string {
	var out = "" //strconv.Itoa(conscienceInfoCount)+"#|#"
	//out+=GetSelfPerceptionInfo()

	var cyclesListStr = ""
	/* сортировать по возрастанию order, можно - по ID циклов всегда возрастает с новым циклом.
	   Но главный - всегда последний (главным может стать любой предшествующий).
	*/
	var mainID = 0
	keys := make([]int, 0, len(cyclesArr))

	for id, v := range cyclesArr {
		if v == nil {
			continue
		}
		if v.isMainCycle {
			mainID = id
		} else {
			keys = append(keys, id)
		}
	}

	sort.Ints(keys)
	var n = 0
	for _, id := range keys {
		if n > 100 {
			ost := len(cyclesArr) - 100
			out += "&nbsp;&nbsp;&nbsp;&nbsp;Еще " + strconv.Itoa(ost) + " циклов..."
			break
		}
		n++
		cyclesListStr += setCicleStr(id)
	}
	if mainID > 0 {
		cyclesListStr += setCicleStr(mainID)
	}

	out += cyclesListStr + "#|#" // для кнопок текущих циклов

	// общая текущая инфа:
	out += "<br>Число параллельных цклов: <b>" + strconv.Itoa(len(cyclesArr)) + "</b><br>"
	out += "<br>Количество Прерываний осознания: <b>" + strconv.Itoa(len(InterruptMemory)) + "</b><br>"
	cStr := ""
	for i := 0; i < len(infoFuncSequence); i++ {
		if i > 0 {
			cStr += ", "
		}
		cStr += strconv.Itoa(infoFuncSequence[i])
	}
	if len(cStr) > 0 {
		out += "последовательность ID выполненных инфо-функций всех циклов: " + cStr + "< br > "
	}

	// текущий актуальный, главный, последний цикл при создании
	if activedCyckleID > 0 {
		out += "<br>Главный цикл мышления  с начальным ID=: " + onClickStr(activedCyckleID, "show_cyckle", "") + "<br>"
		out += GetCycleLocInfo0(activedCyckleID)
	} else {
		out += "<br>Нет главного цикла мышления.<br>"
	}
	return out
}

// //////////////////
func setCicleStr(cID int) string {
	out := ""
	//	cycle,ok:=cyclesArr[cID]
	cycle, ok := ReadecyclesArr(cID)
	if ok {
		if cycle.isMainCycle {
			//mainCycle=&v
			out += "m," + strconv.Itoa(cycle.ID) + "," + strconv.Itoa(cycle.order) + "|"
		} else {
			out += "0," + strconv.Itoa(cycle.ID) + "," + strconv.Itoa(cycle.order) + "|"
		}
	}
	return out
}

// /////////////////////////////////////
// инфа для пульта по get_short_info(
func GetCycleLocInfo0(cID int) string {
	//	c,ok:= cyclesArr[cID]
	c, ok := ReadecyclesArr(cID)
	if ok {
		return c.log
	}
	return "Нет цикла с ID=" + strconv.Itoa(cID)
}

////////////////////////////////////////////////////////////////

/*
	запись инфы о циклах в файл папки пульта /cycle_logs/

по каждому циклу - файл cycle.order+".txt"
С каждым пробуждением папка очищается чтобы начать новый лог.
*/
func updateCycleLogsFiles(c *cycleInfo) {
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/cycle_logs/"+strconv.Itoa(c.order)+".txt", c.log)
}

// //////////////////////////////////////////////////////////////
func clinerCycleLogsFiles() {
	dir := lib.GetMainPathExeFile() + "/cycle_logs/"
	lib.ClinerAllFromDir(dir)
}

////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_functions.go
____________________________________________
/*  функции для  consciousnessThinking(c *cycleInfo)

вынесены коды из блоков consciousnessThinking в отдельные функции чтобы было все видно

Функции обслуживают процесс в consciousnessThinking(c *cycleInfo)
они возвращают true если consciousnessThinking нужно прервать в этом месте
или false если нужно, чтобы consciousnessThinking продолжила далее процесс.

////////////////////////////////////////////////////
func xxxxxProcess(cycle *cycleInfo)bool{

	return false
}
/////////////////////////////////////////////////////
*/

package psychic

import (
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
)

//////////////////////////////////////////////////////////////////

// ////////////////////////////////// ВАЖНЫЙ ОБЪЕКТ ВНИМАНИЯ
func extremImportanceObjectProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "extremImportanceObjectProcess"
	/////////////////  запись в ЛОГ: вытащить *importance по extremImportanceObject.extremObjID
	//oi,ok:=importanceFromID[extremImportanceObject.extremObjID]
	oi := getExtremObjFromID(extremImportanceObject.objID)
	if oi == nil {
		cycle.log += "НЕПРАВИЛЬНЫЙ ОБЪЕКТ ВНИМАНИЯ " + strconv.Itoa(extremImportanceObject.objID) + "...."
	}
	////////////////////

	if oi != nil && oi.extremVal < 3 { // проблемный объект с отрицательным эффектом - ОСНОВА ТВОРЧЕСКОГО ПОИСКА
		// текущий значимый объект внимания с отрицательным эффектом extremImportance.extremVal
		problemExtremImportanceObject = extremImportanceObject
	}
	// не опасно
	if !CurrentInformationEnvironment.veryActualSituation && !CurrentInformationEnvironment.danger {
		if !cycle.dreaming { // нет режима пассивного мышления
			if infoFunc2(cycle) {
				return true
			}
		}

	} else { // ОПАСНО
		//Срочно найти какое-то подходящее действие, получить mentalInfoStruct.runMotorAtmzmID и запустить его
		if infoFunc21(cycle) {
			return true
		} else {
			/* раз не решено в infoFunc21
			Выбрать о чем подумать (НУЖНО ПРИДУМАТЬ ЧТО-ТО НОВОЕ) в infoFunc2(), с учетом предыдущих шагов цикла.
			запустить базовую функцию infoFunc2() поиска по имеющейся информации
			пока не будет запущен мот.автоматизм infoFunc17()
			*/
			if infoFunc2(cycle) {
				return true
			}
		}
	}

	return false
}

/////////////////////////////////////////////////////

////////////////////////////////////////////////////
/* ОБЫЧНАЯ СИТУАЦИЯ, О ЧЕМ ДУМАТЬ
    Выбрать о чем подумать (НУЖНО ПРИДУМАТЬ ЧТО-ТО НОВОЕ) в infoFunc2(), с учетом предыдущих шагов цикла.
    запустить базовую функцию infoFunc2() поиска по имеющейся информации
    пока не будет запущен мот.автоматизм infoFunc17()
   		infoFunc2(cycle)

func usualThinkProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "usualThinkProcess"

	// если сменилась тема или нет цели
	//Ментальное определение ближайшей Цели в данной ситуации по текущей теме

	///////////////////////////////////////////
	//детекция осмысленного ленивого состояния - все еще нет цели и нет доминанты
	if isIdleness() {
		if !cycle.dreaming {
			idlenessType = 2
			cycle.dreaming = true
			cycle.log += "Детекция осмысленного ленивого состояния - все еще нет цели и нет доминанты<br>"
			processingFreeState(false) // как во сне - обработка структур в свободном состоянии может быть долгой
			//чтобы остановить: processingFreeState(true)
			endDereamsCycles() // погасить все циклы дремы
		} //if cycle.dreaming!=1{
		return true
	}

	//if stimulCount > 1{//не было моторного ответа на прошлый стимул, а уже последовавл новый
	if isConfusion { // определяется в func consciousnessElementary()
		cycle.log += "Обработка конфуза: не было моторного ответа на прошлый стимул, а уже последовавл новый<br>"
		isConfusion = false
		interruptMentalWork(cycle.ID) // запомнить чтобы вернуться
		//Новая тема: Непонимание с небольшим весом
		runNewTheme(10, 1)
		cycle.log += "Новая Тема: Непонимание с небольшим весом<br>"
	}

	///////////////////////////////////

	//if currentInfoStructId!=5 { //если уже идет цикл мышления с функцией 5, то не прерывать это мышление
	//Выбрать о чем подумать (НУЖНО ПРИДУМАТЬ ЧТО-ТО НОВОЕ) в infoFunc2(), с учетом предыдущих шагов цикла.
	//запустить базовую функцию infoFunc2() поиска по имеющейся информации
	//пока не будет запущен мот.автоматизм infoFunc17()
	if infoFunc2(cycle) {
		return true
	}

	// по информации или продолжить цикл мышления или остановить после запуска моторного автоматизма
	if mentalInfoStruct.motorAtmzmID > 0 && mentalInfoStruct.motorAtmzmID == wasMentalRunMotorAtmzmID { // был запущен моторный автоматизм
		mentalInfoStruct.motorAtmzmID = 0
		wasMentalRunMotorAtmzmID = 0
		// должен прерываться цикл т.к. был запущен моторный автоматизм
		return true
	} else { // продолжить цикл мышления
		//МЕНТАЛЬНЫЙ АВТОМАТИЗМ - выдать цепочку инфо-фукнций из опыта мент.эпиз.памяти
		//которая потом будет использовать для последовательного запуска инфо-функций в findInfoIdFromExperience()
		mentAtmzmActualFuncs := getFavoritInfoFunc()
		if mentAtmzmActualFuncs != nil && len(mentAtmzmActualFuncs) > 0 {
			if mentAtmzmProcess(cycle) {
				return true
			}
		}
	}

	return false
}
*/
/////////////////////////////////////////////////////

// ////////////// состояние лени
func lenessProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "lenessProcess"
	idlenessType = 1 // гомео-лень, т.е. непроизвольная
	//если есть проблемный, рвущещейся на выполнение автоматизма
	if mentalInfoStruct.motorAtmzmID > 0 {
		return false // даже если лень, то нужно что-то делать, поэто - смотреть далее
	}

	//если нет проблемного, рвущегося на выполнение автоматизма
	if mentalInfoStruct.motorAtmzmID == 0 {
		cycle.dreaming = false
		endAllCycles() //закончить ВСЕ циклы мышления
		cycle.log += "Состояние осоловелости<br>"
		// пофиг все, можно лениво обрабатывать накопившиеся структуры, реагирование - на уровне - до EvolushnStage < 4

		processingFreeState(false) // как во сне - обработка структур в свободном состоянии может быть долгой
		//чтобы остановить: processingFreeState(true)

		return true
	} //if mentalInfoStruct.motorAtmzmID==0{

	return false
}

//////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////

// //////////////////////////////////////////////////
var blockingNewProblemTryCount = false // от быстрого увеличения счетчика проблем в цикле, сбрасывается в func consciousnessElementary()

// только если ВАЖНАЯ ИЛИ ОПАСНАЯ СИТУАЦИЯ  не в дреме Блок повышенной акутальности
func dangerActualProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "dangerActualProcess"
	// некогда думать, нужно что-то срочно решать

	/* newEpisodeMemory(0,0)// для данной detectedActiveLastProblemNodID пишем кадр без Правила */
	// написать нулевое Правило:
	saveNewEpisodic(curActions.ID, 0, 0, 0)

	/* увеличить счетчик нерешенной проблемы
	только для главного цикла и только в момент Стимула с пульта
	чтобы не молотило с каждым циклом.
	*/
	if cycle.isMainCycle && !blockingNewProblemTryCount {
		blockingNewProblemTryCount = true // хорош
		cycle.log += "Увеличен счетчик нерешенной проблемы<br>"
		lib.MapCheckWrite(MapGwardProblemTryCount)
		problemTryCount[detectedActiveLastProblemNodID]++
		lib.MapFree(MapGwardProblemTryCount)
		if EvolushnStage > 4 { //5-я сталия развития -
			// TODO доминанта может быть открыта произвольно сразу - тут нужно происследовать...
			lib.MapCheck(MapGwardProblemTryCount)
			if problemTryCount[detectedActiveLastProblemNodID] > 3 && CurrentInformationEnvironment.veryActualSituation {
				// создать доминанту
				createDominanta(cycle)
				return true
			}
		}
	}

	infoFunc25(cycle) //Посмотреть условия чтобы проявить инициативу

	// нет лени и нет штатного автоматизма
	if idlenessType == 0 && !mentalInfoStruct.noStaffAtmzmID {
		// Срочно найти какое-то подходящее действие, получить mentalInfoStruct.runMotorAtmzmID и запустить его
		if infoFunc21(cycle) { // если найдено, то останов цикла
			return true
		}
	}

	/*МЕНТАЛЬНЫЙ АВТОМАТИЗМ - выдать цепочку инфо-фукнций из опыта мент.эпиз.памяти
	которая потом будет использовать для последовательного запуска инфо-функций в findInfoIdFromExperience()
	*/
	mentAtmzmActualFuncs := getFavoritInfoFunc()
	if mentAtmzmActualFuncs != nil && len(mentAtmzmActualFuncs) > 0 {
		if mentAtmzmProcess(cycle) {
			return true
		}
	}

	return false
}

/////////////////////////////////////////////////////

// //////////////////////////////////////////////////
// творчество, мышление о Доминанте, фантазия - как функция интенсивного подбора ассоциаций
func dominantsProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "dominantsProcess"
	// cycle.log+=" Отработка четвертого уровня осмысления с ID циикла "+strconv.Itoa(cycle.ID)+" мышления  "+onClickStr(cycle.ID,"show_cyckle","")+"<br>"
	// уже может быть запущена доминанта нерешенной проблемы
	// и тут - Стек отложенных дел.

	/* !!! при isRepressionStimulsNoise = true
	нет решения доминанты по аналогии т.к. заблокированы стимулы,
	Это - режим творческого решения по ментальным правилам или попытки новых действий.
	*/
	if isRepressionStimulsNoise {

	}

	if !CurrentInformationEnvironment.danger { // цикл решения доминирующей проблемы
		//выбор подходящей для detectedActiveLastProblemNodID доминанты ()
		//CurrentProblemDominanta=
		// TODO для решения по ID дерева проблем учитывать: getCurSituation(), getCurTheme() и getCurPurpose()
		infoFunc28(cycle)
	}
	return false
}

/////////////////////////////////////////////////////

////////////////////////////////////////////////////
/*  использовать ментальный автоматизм mentAtmzmActualFuncs:=getFavoritInfoFunc(), привязанный к текущей ветке дерева проблем

 */
func mentAtmzmProcess(cycle *cycleInfo) bool {
	cycle.lastProcessID = "mentAtmzmProcess"
	// есть ли полезный мент.автоматизм в узле дерева проблем, если есть сравнить полезность
	if mentAtmzmActualFuncs == nil || len(mentAtmzmActualFuncs) == 0 {
		return false
	}
	maStr := ""
	for i := 0; i < len(mentAtmzmActualFuncs); i++ {
		if i > 0 {
			maStr += ", "
		}
		maStr += strconv.Itoa(mentAtmzmActualFuncs[i])
	}
	cycle.log += "Отработка мент.автоматизма " + maStr + ", прикрепленного к ветве дерева проблем.<br>"

	/* с учетом модели понимания UnderstandingModel=make(map[int][]int) с индексом == ID объекта внимания extremImportance
	   а так же значимостей данного объета extremImportance
	   текущей Темы и Цели	(сужающий контекст)	т.е. учет правил только ID ветки проблем,
	   но при неудаче - смотреть и дургие ветки проблем.

	      (учти, что цикл мышления в автоматизме - то же самое, что ментальное правило!)
	      	чтобы не совершать снова работу по поиску подходящего ментального правила - использовать цикл из автоматизма.

	   Фактически мент.автоматизм выполняет роль поддержки Доминанты + пополняет массив tryActionArr.
	   Сначала просто пытается оптимизировать решение по инфе своего цикла,
	   потом открывается Доминанта и все временные решения заливаются для уже доимнанитного решения.
	*/

	/* TODO постепенно вбрасывать функции, а не хором
	for i:=0;i<len(mentAtmzmActualFuncs);i++ {
		funcID := mentAtmzmActualFuncs[i]
		if funcID > 0 {
			if runMentalFunctionID(cycle, funcID) {
				return true
			}
		}
	}*/

	// если найден новый вариант мышления, то УСТАНОВИТЬ НУЖНУЮ ИНФУ и запусть цикл с предполагаемым продолжением
	// в этом случае здесь -  return false

	return false
}

/////////////////////////////////////////////////////

///////////////////////////////////
/* по правилу найти или создать (в случае AmtzmNextString) автоматизм и запустить его
Если совершено действие - возвращает true
*/
func makeActionFromRooles(rules Rule) bool {
	// по правилу найти или создать (в случае AmtzmNextString) автоматизм и запустить его
	var atmzm *Automatizm

	conscienceStatus += "Найдено Правило по которому можно совершить действие.<br>"

	// выбираем Ответное действие из Правила чтобы повторить его
	//ai := ActionsImageArr[ta.Action]
	ai, ok := ReadeActionsImageArr(rules.Action)
	if ok {
		/////////////////////////////////
		if ai.Kind == 1 { // субъективный образ
			// Правило может быть неадекватным реальности
			//запускать автоматизм в опредеелнных условиях:
			if !CurrentInformationEnvironment.veryActualSituation &&
				!CurrentInformationEnvironment.danger &&
				//есть ли данные Базовые контексты 2 Поиск, 3 Игра, 4 Гон, 5 Защита - в составе текущей эмоции
				existsBaseContextFromEmotionID([]int{2, 3, 4, 5}, CurrentInformationEnvironment.PsyEmotionId) {
				// можно использовать субъективный образ
				purpose := getPurposeGenetic()
				purpose.actionID = ai
				atmzm = createAndRunAutomatizmFromPurpose(purpose)
			}
		} //if ai.Answer==1 { // субъективный образ
		/////////////////////////////////
	}

	if atmzm != nil {
		automatizmCorrection(atmzm, rules.Effect, nil)

		// вытащить образ действий успешного автоматизма и попробовать решить подходящую по аналогии Домимнату
		checkRelevantAction(curActions.ID, atmzm.ActionsImageID, atmzm.Usefulness)
		mentalInfoStruct.motorAtmzmID = 0 // сброс рассматривания автоматизма
		mentalInfoStruct.noStaffAtmzmID = false
		automatizmStatus = 0
		// если было размышление, то оно не прерывается в своем рекурсивном проходе
		conscienceStatus += "Запущен альтернативный штатному автоматизм, найденный в Правилах.<br>"
		atmtzmActualTreeNode = atmzm
		motorActionEffect = 1 // чтобы не запустилась infoFunc13
		return true           // блокировать рвущийся автоматизм т.к. запущен другой вместо него
	}

	return false
}

//////////////////////////////////////////////////

/*
	Для текущей фразы сначала смотрим есть ли для данных условий (BaseID + EmotionID) автоматзмы для известных слов фразы.

Если нет, то по Правилам выбираются подходяшие дейстивия.
Если найдены действия, то создается автоматизм, который прикрепляется к узлу дерева автоматизмов branchID.
В случае создания автоматизма возвращает autmtzm.ID иначе - 0.
Если есть автоматизмы на "привет" и на "как дела?" то на "привет как дела?" должен сформироваться автоматизм из их действий.
Если есть автоматизмы на "привет", но нет автоматизма на "как дела?" то выдаст автоматизм на "привет"

Работает с произвольным branchID, а не только при активации дерева,
так что функцию можно испольавтоматизмы на "привет"зовать для мыслительной произвольности
*/
func tryCreateAnswerForPhrese(limitActions int, branchID int) (int, *Automatizm) {
	wIDarr := wordSensor.CurretWordsIDarr // // массив wordID слов, вместо нераспознанных -1
	if wIDarr == nil {
		return 0, nil
	}
	//выбрать только известные слова
	var wrID []int
	for i := 0; i < len(wIDarr); i++ {
		if i > 10 { // смотрим не более, чем по 10 слов для данного уровня
			break
		}
		if wIDarr[i] > 0 {
			wrID = append(wrID, wIDarr[i])
		}
	}
	wIDarr = wrID
	// сформировать последовательность известных фраз из этих слов
	var phraseArr []int // знакомые фразы для поиска в дереве автоматизмов
	var tmpArr []int    // набор слов для распознавания фразы
	for n := 0; n < len(wIDarr); n++ {
		tmpArr = nil
		// сдвигаем на следующее слово
		for i := n; i < len(wIDarr); i++ {
			tmpArr = append(tmpArr, wIDarr[i])
			phraseID := wordSensor.GetExistsPraseIDFromWordArr(tmpArr) // распознаватель фразы без записи нового
			if phraseID > 0 {
				phraseArr = append(phraseArr, phraseID)
			}
		}
	}
	if phraseArr == nil {
		return 0, nil
	}

	////////////////  далее - последовательность сопособов получить autmtzm.ID

	/*1 способ: выбрать для данных условий (BaseID + EmotionID) автоматзмы для известных фраз
	 */
	emotionNode := getNodeFromLevel(2, branchID) // вернуться назад до узла эмоций и оттуда смотреть все
	if emotionNode != nil {
		/* начать поиск с данного узла по распознаннм фразам поиском по дереву автоматизмов
		Если в проходе есть автоматизм, то он учитывается.
		*/
		var aArr []int // для каждого известного слова выбрать ID действий
		var atmtzmFirst *Automatizm
		for i := 0; i < len(phraseArr); i++ {
			atmtzm := getAtmtzmFromNodesFrase(emotionNode, phraseArr[i])
			if atmtzm != nil {
				if !lib.ExistsValInArr(aArr, atmtzm.ActionsImageID) { // убираем дублеры
					aArr = append(aArr, atmtzm.ActionsImageID)
				}
				if atmtzmFirst == nil {
					atmtzmFirst = atmtzm
				}
			}
		}

		if aArr != nil && atmtzmFirst != nil {
			// для случая, когда во фразе нашелся фрагмент, на который есть автоматизм
			if len(aArr) == 1 {
				if isUnrecognizedPhraseFromAtmtzmTreeActivation { // просто выдаем автоматизм потому, что AutomatizmTreeFromID[branchID].PhraseID==0 - создаст кривой автоматизм без действий и фразы
					return atmtzmFirst.ID, atmtzmFirst
				}
				aID, atzm := createNewAutomatizmID(0, branchID, atmtzmFirst.ActionsImageID, true)
				if atzm != nil {
					return aID, atzm
				}
			} else {
				//создать автоматизм из цепочки действий, если действий >1, то - c atzm.NextID
				aID, atzm := createAutomatizmFromNextString(aArr, branchID)
				if atzm != nil {
					return aID, atzm
				}
			}
		}
	}
	//.......................

	// phraseArr=[]int{132,1229} // проверка
	//2 способ: выбрать действия из позитивных правил по каждой фразе
	var rwArr []int // для каждого известной фразы выбрать ID действий

	for i := 0; i < len(phraseArr); i++ {
		// позитивные правила для данного ID слова
		rArr := getWellRoolesFromPhraseId(phraseArr[i], true) // для текущих условий
		if rArr == nil {
			rArr = getWellRoolesFromPhraseId(phraseArr[i], false) // без учета условий
		}
		if rArr == nil {
			continue
		}
		// выбрать самые эффективные
		max := 0 // наяиная с эффективности 1
		actID := 0
		for n := 0; n < len(rArr); n++ {
			if rArr[n].Effect > max {
				max = rArr[n].Effect
				actID = rArr[n].Action
			}
		}
		// убираем дублеры
		if !lib.ExistsValInArr(rwArr, actID) {
			rwArr = append(rwArr, actID)
		}
	}

	// последовательность действий для автомтаизма
	var aArr []int
	for i := 0; i < len(rwArr); i++ {
		if rwArr[i] == 0 {
			continue
		}
		if i > limitActions {
			break
		}
		aArr = append(aArr, rwArr[i])
	}
	// создать цепочку действий
	if aArr == nil {
		return 0, nil
	}
	//id,_:=createAmtzmNextStringID(0,aArr,true)// создать образ цепочки типа AmtzmNextString

	//создать автоматизм из цепочки действий
	aID, atzm := createAutomatizmFromNextString(aArr, branchID)
	if atzm != nil {
		return aID, atzm
	}

	return 0, nil
}

/////////////////////////////////////////////////////

/*
	прогноз по действию автоматизма из последнего кадра эпиз.памяти

Возвращает эффект последнего позитивного правила в группе исторических кадров.
Пример:
Оператор: «ты кто?»
Beast: «бот»
Оператор: «уверен?»
Beast: «да»
Позитив группового правила

Оператор: «кто ты?»
Beast: «Бог»
Оператор: «уверен?»
Beast: «да»
Негатив группового правила

Казалось бы при проверке группового правила учитывать предыдущий диалог
чтобы при "уверен?" проверялось только то правило, где есть предыдущий диалог.
Но при реальной проверки диалог, который начинается на "уверен?" (даже повторенный 2 раза подряд)
всегда дает эффект == 0 и выполняется автоматизм "да".
Так что пока нет необходимости собирать все групповые правила с заданным диалогом и выбирать наиболее подходящее.
Но это еще не точно, в каких-то случаях такое может приводить к неточности, это нужно проверить TODO
*/
func getPrognoze(atmtzm *Automatizm) (int, int) {
	if atmtzm == nil {
		lib.TodoPanic("Нулевое значение параметра в func getPrognoze")
	}
	if curActiveActionsID == 0 { // стимул от гомеостаза, а не от оператора
		return 0, 0
	}
	actionID := atmtzm.ActionsImageID
	//если trigger == actionID ЗНАЧИТ БОТ ЗЕРКАЛИТ стимул
	trigger := curActiveActionsID // активация дерева оператором

	// прямое предсказание последствий выполнения действия автоматизма, т.е. эффект trigger, actionID
	accuracy, effect := getPrognoseFromAutmtzmAction(trigger, actionID)
	if accuracy == 1 { //точное предсказание для действия
		if effect < 0 { // негативное предсказание
			// но потом будет позитив?
			accuracy0, effect0 := positiveFromActionAfterStimul(curActiveActionsID, actionID)
			// если конечный эффект превышает прямой негативный
			if accuracy0 > 0 && effect0 > 0 && effect0 > -effect { //есть превышающее позитивное предсказание
				return accuracy0, effect0
			}
		}
		if effect > 0 { // позтивное предсказание
			// но потом будет негатив?
			accuracy0, effect0 := negativeFromActionAfterStimul(curActiveActionsID, actionID)
			// если конечный эффект превышает прямой негативный
			if accuracy0 > 0 && effect0 < 0 && -effect0 > effect { //есть превышающее негативное предсказание
				return accuracy0, effect0
			}
		}
		return accuracy, effect
	}
	if accuracy == 2 { //менее точное предсказание для действия
		//менее точное предсказание для действия, совершенное после Стимула curStimulImageID в данных условиях
		// нет смысла смотреть, чем кончится цепочка кадров
		return accuracy, effect
	}
	if accuracy == 3 {
		//неточное предсказание для действия при любом стимуле и любых условиях
		// дает ли действие негативный эффект ()
		accuracy, effect = negativeFromActionAfterStimul(trigger, actionID)
		if accuracy > 0 {
			return accuracy, effect
		}
	}

	return 0, 0
}

/* СТАРАЯ ВЕРСИЯ
func getPrognoze(atmtzm *Automatizm) (int, int) {
	if atmtzm == nil {
		lib.TodoPanic("Нулевое значение параметра в func getPrognoze")
	}
	actionID := atmtzm.ActionsImageID
	//	accuracy,effect:=positiveFromActionAfterStimul(curActiveActionsID,actionID)
	//Алгоритм:
	 // Поиск цепочек, заканчивающихся большим негативом (превышающим позитив промежуточных)
	 // Если не найдены цепочки, то поиск негатива в правилах с учетом эмоций и Стимула Trigger
	 // Если нет таких, то поиск правил с участием Action
	 // т.е. в первую очередь смотрим нет ли негатива и если нет, то автоматизм может запускаться.
accuracy, effect := getPrognoseFromAutmtzmAction(curActiveActionsID, actionID)
if accuracy == 1 {
//точное предсказание для действия, которое может привести к негативу, но потом будет позитив
}
if accuracy == 2 {
//точное предсказание для действия, совершенное после Стимула curStimulImageID в данных условиях
}
if accuracy == 3 {
//статистически самое позитивное предсказание для действия при любом стимуле и любых условиях
}
if accuracy > 0 {
return accuracy, effect
}
/////////// дает ди действие негативный эффект
accuracy, effect = negativeFromActionAfterStimul(curActiveActionsID, actionID)
if accuracy > 0 {
return accuracy, effect
}

return 0, 0
}
*/

///////////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_model.go
____________________________________________
/* Модель понимания объектов внимания (типа ActionsImage), имеющих значимость в правилах эпиз.памяти.
Показ на пульте - в episodic_memory_pult.go - func Get_undestand_model_from_object

Модель понимани для данного объекта - это все кадры эпизодической памяти EpisodicTreeNode, где он участвует.
В каждом кадре есть связь с тем, к чему привели недавние действия (следующие эпизоды памяти после ObjModel.ID)
и есть полное описание условий того момента (из EpisodicTreeNodeFromID[ObjModel.ID]).

Становится возможным использовать схожесть моделей (совместное учатие в эпизоде) для предположения свойств.

В отличие от значимотей объектов внимания (importance.go)
здесь рассматриваются только эффекты правил, в которых участвует объект.
*/

package psychic

/////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////

/*
	Элемент отдельного эпизода с Правилами типа объект данного типа (лштв) -> эффект в условиях кадра ID эп.памяти

для вывода всех таких Правил в эпиз.памяти - Модель понимания значимости объекта.
Используется для выборки Модели понимания Правил
*/
type RulesModel struct { //
	ID     int // ID эпиз памяти
	Stimul int // Стимул
	Answer int // Ответ
	Effect int // эффект от действий
	Count  int // сила эффекта - число подтверждений
	//Importence int // значимость стимула (Trigger если Effect<100 или Action если Effect==100) -  не используется здесь
}

/*
	Временный массив создается только на время его инфо-обработки в understanding_model_rules.go

Перед каждой выборкой очищается UnderstandingRulesModel = nil
Для каждого экстремального объекта делается своя выборка кадров при сканировании всего дерева эпиз.памяти.
*/
var UnderstandingRulesModel []RulesModel

///////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_model_functions.go
____________________________________________
/*
Модели понимания - функции
*/
package psychic

////////////////////////////////////////////////////

/*
	поиск решения по Моделям понимания

Использовать модели объектов для нахождения действий ПО АНАЛОГИИ с теми действиями,
что были в эпиз.памяти для данного объекта.
Леонардо да Винчи смотрел на штукатурку чтобы в голову пришла идея.

При удачном поиске будет создан и запущен автоматизм.
*/
func tryModelsLevel(c *cycleInfo) bool {

	actBest, _ := bestActionFromExtremImportanceObject()
	if actBest != nil { // найдено лучшее
		//получить пользу из эффекта лучшего моторного действия: будет найден и запущен лучший мот.автоматизм aBest.ID
		return getBenefitFromEpizosMemory(c, actBest)
	}
	return false
}

//////////////////////////////////////////////////

/*
найти действие в данных условиях (bestActionFromExtremImportanceObject()).
которое лучше, чем текущий extremImportanceObject
*/
func bestActionFromExtremImportanceObject() (*ActionsImage, int) {
	if extremImportanceObject == nil {
		return nil, 0
	}
	getRulesModelExactly(extremImportanceObject.objID)
	if UnderstandingRulesModel == nil {
		getRulesModelApproximately(extremImportanceObject.objID)
	}
	if UnderstandingRulesModel == nil {
		return nil, 0
	}
	///////////
	var effectMax = 0
	var actBest *ActionsImage

	for i := 0; i < len(UnderstandingRulesModel); i++ {
		rm := UnderstandingRulesModel[i]
		eff := getWpower(rm.Effect, rm.Count)
		if eff > effectMax {
			//actBest=ActionsImageArr[rm.Action] // ответ с хорошим эффектом
			actN, ok := ReadeActionsImageArr(rm.Answer)
			if !ok {
				continue
			}
			effectMax = eff
			actBest = actN // ответ с хорошим эффектом
		}
	}
	return actBest, effectMax
}

///////////////////////////////////////////////////////////

/*
выбрать все позитивные образы действий из extremImportanceObject
*/
type bestActionArr struct {
	action *ActionsImage
	effect int
}

func bestActionArrFromExtremImportanceObject() []bestActionArr {
	if extremImportanceObject == nil {
		return nil
	}
	getRulesModelExactly(extremImportanceObject.objID)
	if UnderstandingRulesModel == nil {
		getRulesModelApproximately(extremImportanceObject.objID)
	}
	if UnderstandingRulesModel == nil {
		return nil
	}
	///////////
	var out []bestActionArr

	for i := 0; i < len(UnderstandingRulesModel); i++ {
		rm := UnderstandingRulesModel[i]
		if rm.Effect > 0 {
			//actBest=ActionsImageArr[rm.Action] // ответ с хорошим эффектом
			actN, ok := ReadeActionsImageArr(rm.Answer)
			if !ok {
				continue
			}
			out = append(out, bestActionArr{actN, rm.Effect}) // ответ с хорошим эффектом
		}
	}
	return out
}

///////////////////////////////////////////////////////////

/*
попробовать для случая негативного obj *extremImportance или emExtrem *EpisodeMemory
Из нескольких акций
собрать один образ действий типа ActionsImage - из составляющих элементов с позитивными эффектами цепочки Правил
*/
func createSintezActiveActionsFromExtremImportanceObject() (ActionsImage, bool) {
	var action ActionsImage
	aArr := bestActionArrFromExtremImportanceObject()
	if aArr == nil {
		return action, false
	}
	aCount := len(aArr)
	if aCount == 1 {
		return *aArr[0].action, true
	}
	/* собрать образ действий типа ActionsImage.ID из составляющих элементов,
	вытаскивая из всех образов составляющие и выбирая из лучших один сложный образ действия.
	*/
	beast := 0
	toneID := 0
	moodID := 0

	for i := 0; i < aCount; i++ {
		for j := 0; j < len(aArr[i].action.ActID); j++ {
			action.ActID = append(action.ActID, aArr[i].action.ActID[j])
		}
		for j := 0; j < len(aArr[i].action.ActID); j++ {
			if aArr[i].action.PhraseID != nil {
				action.PhraseID = append(action.PhraseID, aArr[i].action.PhraseID[j])
			}
		}
		if aArr[i].effect > beast {
			beast = aArr[i].effect
			toneID = aArr[i].action.ToneID
			moodID = aArr[i].action.MoodID
		}
	}
	action.ToneID = toneID
	action.MoodID = moodID

	return action, true
}

////////////////////////////////////////////////////////////////////

/*
	посмотреть максимальный эффект в Модели понимания

для данного образа действия типа ActionsImage
в данных условиях дерева проблем.
*/
func getExtremEffectFromModel(actImgID int) int {
	if actImgID == 0 || detectedActiveLastProblemNodID == 0 {
		return 0
	}

	getRulesModelFromActionExactly(actImgID)
	if UnderstandingRulesModel == nil {
		return 0
	}
	///////////
	var effectMax = 0

	for i := 0; i < len(UnderstandingRulesModel); i++ {
		rm := UnderstandingRulesModel[i]
		eff := getWpower(rm.Effect, rm.Count)
		if eff > effectMax {
			effectMax = eff
		}
	}
	if effectMax != 0 { // найдено лучшее
		return effectMax
	}
	return 0
}

/////////////////////////////////////////////////////////////////

/*
	плохой ли выбор actionsImageID для мовершения действий

true - не подходящий для данного detectedActiveLastProblemNodID

Проверка только для tryModelsLevel_1() с точным результатом
*/
func isBadActionsImagefromTryActionArr(actImgID int) bool {
	getRulesModelFromActionExactly(actImgID)
	if UnderstandingRulesModel == nil {
		return false // если нет еще опыта, будем считать, что пойдет
	}
	///////////
	for i := 0; i < len(UnderstandingRulesModel); i++ {
		rm := UnderstandingRulesModel[i]
		if rm.Effect < 0 {
			return true
		}
	}
	// если нет плохого эффекта, будем считать, что пойдет
	return false
}

///////////////////////////////////////////

/*
	посмотреть, насколько опасны последствия в Модели понимания

для данного образа действия типа ActionsImage
в данных условиях дерева проблем.
*/
func isDangerousModelAutomatism(actImgID int) int {
	if detectedActiveLastProblemNodID == 0 {
		return 0
	}

	getRulesModelFromActionExactly(actImgID)
	if UnderstandingRulesModel == nil {
		return 0
	}
	///////////
	var effectMin = 0

	for i := 0; i < len(UnderstandingRulesModel); i++ {
		rm := UnderstandingRulesModel[i]
		eff := getWpower(rm.Effect, rm.Count)
		if eff < effectMin {
			effectMin = eff
		}
	}
	/*так как эффект отрицательный потому, что негативный, переводим его в значение по модулю,
	  чтобы потом сравнивать не плохой с хорошим, а определять позитивный эфеект, ПРЕВЫШАЮЩИЙ негативный
	  только в этом случае стоит бездумно отдавать ему предпочтение, но только в прогнозе на 1 шаг:
	  разрешить сделать шаг назад, если потом СРАЗУ ЖЕ будет 2 шага вперед
	  более сложный анализ предполагается в более продвинутых функциях (хотя может быть сложнее и нет ничего)
	*/
	effectMin = -1 * effectMin
	return effectMin
}

/////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_model_pult.go
____________________________________________
/* Вывод моделей понимания на Пульт

 */

package psychic

import (
	"strconv"
)

/*
func GetMentalUndestandingModelsForPult()string{
var out=""
	getAllExtrОbjIDarr()
if UnderstandingModelArr==nil{
	return "Нет Моделей понимания в эпиз.памяти."
}

	// этот вывод далается при пульсации, и чтобы не прыгало случайным образом на пульте нужно делать сортировку
	keys := make([]int, 0, len(UnderstandingModelArr))
	for id,v:= range UnderstandingModelArr {
		if v==nil{continue}
		keys = append(keys, id)
	}

	sort.Ints(keys)
	for _, id := range keys{
		arr,ok:=ReadeUnderstandingModelArr(id)
		if !ok {
			continue
		}
		out += "<b>Образ ID=<b> <span style='cursor:pointer;color:blue' onClick='get_ment_model_index(" + strconv.Itoa(id) + ")'>" + strconv.Itoa(id) + "</span>" + ":</b> "
		out += "<b>Кадры: <b>"
		for i:=0; i < len(arr); i++ {
			if i > 0 {
				out += ", "
			}
			out += "<span style='cursor:pointer;color:blue' onClick='get_epiz_memory_info(" + strconv.Itoa(arr[i]) + ")'>" + strconv.Itoa(arr[i]) + "</span>" + "</b>"
		}
		//out += ","
		out += "<br>\r\n"
	}
	return out
}
*/

func GetModelExtremImportanceInfo(id int) string {
	/*curImportanceObjectArr не сохраняется, а возникает только в данной сессиии
	Сохраняется extremImportance.extremObjID в importance.txt
	Поэтому id - это importance
	*/
	var out = ""
	//etremObj:= getExtremObjFromID(id)
	//	obj,ok:=importanceFromID[id]
	obj := getExtremObjFromID(id)
	if obj == nil {
		out += "ID объекта типа importance " + strconv.Itoa(id) + " не существует.<br>"
	} else {
		out += "ID объекта типа importance " + strconv.Itoa(id) + ":<br>"
		out += GetImportanceObjectInfo(obj.objID)
	}

	return out
}

// узел дерева проблем
func GetProblemTreeForNodeInfo(id int) string {
	var out = ""
	//	node:= ProblemTreeNodeFromID[id]
	node, ok := ReadeProblemTreeNodeFromID(id)
	if !ok {
		out += "Узел дерева проблем ID=" + strconv.Itoa(id) + " не существует.<br>"
	} else {
		out += "Узел дерева проблем: ID=" + strconv.Itoa(id) + ":<br>"

		out += "Конечный узел активной ветки дерева моторных автоматизмов: <span style='cursor:pointer;color:blue' onClick='show_atmzm_tree(" + strconv.Itoa(node.autTreeID) + ")'>" + strconv.Itoa(node.autTreeID) + "</span>" + "<br>\n"
		out += "Конечный узел активной ветки дерева ситуации: <span style='cursor:pointer;color:blue' onClick='show_unde_tree(" + strconv.Itoa(node.situationTreeID) + ")'>" + strconv.Itoa(node.situationTreeID) + "</span>" + "<br>\n"
		out += "Тема: <span style='cursor:pointer;color:blue' onClick='get_theme(" + strconv.Itoa(node.themeID) + ")'>" + strconv.Itoa(node.themeID) + "</span>" + "<br>\n"
		out += "Цель: <span style='cursor:pointer;color:blue' onClick='get_purpose(" + strconv.Itoa(node.purposeID) + ")'>" + strconv.Itoa(node.purposeID) + "</span>" + "<br>\n"
	}

	return out
}

____________________________________________

BOT/brain/psychic/understanding_model_rules.go
____________________________________________
/* Выборки из эпизодической памяти Моделей понимания
ВЫБОРКИ КАДРОВ С ПРАВИЛАМИ, в которых Trigger есть ПОЛНЫЙ образ восприятия

Типичная апись - формирует элемнты Правила модели для объекта:
saveNewEpisodic(maxE.extremObjID,maxE.kind,maxE.extremVal,1) - элемент модели понимания в условиях его выявления


Обновление массива var UnderstandingModel []ObjModel
*/

package psychic

import (
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
)

//////////////////////////////////////////////////////////////////////

/*
найти лучший образ действий для стимула extremImportanceObject ТОЧНО
Выборка эпизодов Модели понимания, содержащими Stimul типа ActionsImage в качестве Стимула

ТОЧНО ДЛЯ ТЕКУЩИХ УСЛОВИЙ NodeAID,NodeSID,NodePID
Найти все эпизоды с Stimul типа ActionsImage в качестве Стимула,
т.е. с Trigger == Stimul и node.PARAMS[0][2]>0
Результат - в виде массива Правил UnderstandingRulesModel []RulesModel

Быстрая.
*/
func getRulesModelExactly(objID int) {
	if objID == 0 {
		lib.TodoPanic("НУЛЕВОЙ ПАРАМЕТР Stimul в func getRulesModelExactly!")
		return
	}
	UnderstandingRulesModel = nil

	cond := []int{detectedActiveLastNodID,
		detectedActiveLastUnderstandingNodID,
		detectedActiveLastProblemNodID,
		objID}
	id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
	if id == 0 {
		return
	}
	node, ok := ReadeEpisodicTreeNodeFromID(id)
	if !ok {
		return
	}

	scanEpizRulesChildrens(node)
}

// вытащить все ID узлов, в которых есть условие, начиная с node
func scanEpizRulesChildrens(node *EpisodicTreeNode) {
	for _, child := range node.Children {

		if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
			om := RulesModel{child.ID, child.Trigger, child.Action, child.PARAMS[0], child.PARAMS[1]}
			UnderstandingRulesModel = append(UnderstandingRulesModel, om)
		}
		// продолжим рекурсивно искать в нем далее
		scanEpizRulesChildrens(&child)
	}
	return
}

/////////////////////////////////////////////////////////////////////////////////

/*
найти лучший образ действий для стимула extremImportanceObject ПРИБЛИЗИТЕЛЬНО (только для условия detectedActiveLastNodID)
Выборка эпизодов Модели понимания с Правилами, содержащими Stimul типа ActionsImage

ПРИБЛИЗИТЕЛЬНО ДЛЯ ТЕКУЩИХ УСЛОВИЙ NodeAID
Найти все эпизоды с Stimul типа ActionsImage в качестве Стимула,
т.е. с Trigger == Stimul и node.PARAMS[0][2]>0
Результат - в виде массива Правил UnderstandingRulesModel []RulesModel

Быстрая.
*/
func getRulesModelApproximately(objID int) {
	if objID == 0 {
		lib.TodoPanic("НУЛЕВОЙ ПАРАМЕТР Stimul в func getRulesModelApproximately!")
		return
	}
	UnderstandingRulesModel = nil

	cond := []int{detectedActiveLastNodID}
	id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
	if id == 0 {
		return
	}
	node, ok := ReadeEpisodicTreeNodeFromID(id)
	if !ok {
		return
	}

	scanEpizRulesChildrens2(node, objID)
}

// вытащить все ID узлов, в которых есть условие, начиная с node
func scanEpizRulesChildrens2(node *EpisodicTreeNode, stimul int) {
	for _, child := range node.Children {

		if len(child.PARAMS) > 0 { // узел с прописанным PARAMS
			if child.Trigger != stimul {
				continue
			}
			om := RulesModel{child.ID, child.Trigger, child.Action, child.PARAMS[0], child.PARAMS[1]}
			UnderstandingRulesModel = append(UnderstandingRulesModel, om)
		}
		// продолжим рекурсивно искать в нем далее
		scanEpizRulesChildrens(&child)
	}
	return
}

/////////////////////////////////////////////////////////////////////////////////

/*
	Выборка эпизодов Модели понимания , содержащими объект типа actImgID для ответного действия

ТОЧНО ДЛЯ ТЕКУЩИХ УСЛОВИЙ NodeAID,NodeSID,NodePID
Найти все эпизоды с объект типа actImgID,
т.е. с Trigger == actImgID и node.PARAMS[0][2]>0
Результат - в виде массива Правил UnderstandingRulesModel []RulesModel

Быстрая.
*/
func getRulesModelFromActionExactly(actImgID int) {
	if actImgID == 0 {
		lib.TodoPanic("НУЛЕВОЙ ПАРАМЕТР actImgID в func getRulesModelFromActionExactly!")
		return
	}
	UnderstandingRulesModel = nil

	cond := []int{detectedActiveLastNodID,
		detectedActiveLastUnderstandingNodID,
		detectedActiveLastProblemNodID}
	id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
	if id == 0 {
		return
	}
	node, ok := ReadeEpisodicTreeNodeFromID(id)
	if !ok {
		return
	}

	scanEpizRulesFromActionChildrens(node, actImgID)
}

// вытащить все ID узлов, в которых есть условие, начиная с node
func scanEpizRulesFromActionChildrens(node *EpisodicTreeNode, actImgID int) {
	for _, child := range node.Children {

		if node.PARAMS != nil && len(node.PARAMS) > 0 { // узел с прописанным PARAMS
			if node.Action != actImgID {
				continue
			}
			om := RulesModel{child.ID, child.Trigger, child.Action, child.PARAMS[0], child.PARAMS[1]}
			UnderstandingRulesModel = append(UnderstandingRulesModel, om)
		}
		// продолжим рекурсивно искать в нем далее
		scanEpizRulesFromActionChildrens(&child, actImgID)
	}
	return
}

/////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////

/*
	Выборка эпизодов Модели понимания, содержащими СЛОВО в Стимуле (Trigger)

ТОЧНО ДЛЯ ТЕКУЩИХ УСЛОВИЙ
Найти все эпизоды с Stimul типа ActionsImage в качестве Стимула,
т.е. с Trigger содержащие wordID
Результат - в виде массива Правил UnderstandingRulesModel []RulesModel
*/
type WordModel struct {
	maxEffect    int // наилучший эффект слова
	minEffect    int // наихудший эффект слова
	beastFrameID int // ID наилучшего по эффекту кадра
	worstFrameID int // ID наихудчшего по эффекту кадра
	beastCount   int // уверенность наилучшего по эффекту кадра
	worstCount   int // уверенность наихудчшего по эффекту кадра
}

var maxEffect = 0
var minEffect = 0
var beastFrameID = 0
var worstFrameID = 0
var beastCount = 0 // уверенность наилучшего по эффекту кадра
var worstCount = 0 // уверенность наихудчшего по эффекту кадра

func getWordModelExactly(wordID int) WordModel {
	out := WordModel{0, 0, 0, 0, 0, 0}
	if wordID == 0 {
		lib.TodoPanic("НУЛЕВОЙ ПАРАМЕТР wordID в func getWordModelExactly!")
		return out
	}
	maxEffect = 0
	minEffect = 0
	beastFrameID = 0
	worstFrameID = 0
	beastCount = 0
	worstCount = 0

	cond := []int{detectedActiveLastNodID,
		detectedActiveLastUnderstandingNodID,
		detectedActiveLastProblemNodID}
	id, _ := findEpisodicBrange(0, cond, &EpisodicTree)
	if id == 0 {
		return out
	}
	node, ok := ReadeEpisodicTreeNodeFromID(id)
	if !ok {
		return out
	}

	scanEpizRulesWords(node, wordID)
	out.maxEffect = maxEffect
	out.minEffect = minEffect
	out.beastFrameID = beastFrameID
	out.worstFrameID = worstFrameID
	out.beastCount = beastCount
	out.worstCount = worstCount
	return out
}

// вытащить все ID узлов, в которых есть условие, начиная с node
func scanEpizRulesWords(node *EpisodicTreeNode, wordID int) {
	for _, child := range node.Children {

		if len(child.PARAMS) > 0 { // узел с прописанным PARAMS

			var existsWord = false
			trigger, ok := ReadeActionsImageArr(child.Trigger)
			if ok {
				if existsWordInPraseArr(wordID, trigger.PhraseID) {
					existsWord = true
				}
			}
			if trigger.PhraseID != nil { //Verbal - при активации дерева автоматизмов
				varb, ok := ReadeVerbalFromIdArr(trigger.PhraseID[0])
				if ok {
					if existsWordInPraseArr(wordID, varb.PhraseID) {
						existsWord = true
					}
				}
			}

			if !existsWord {
				continue
			}
			eff := getWpower(child.PARAMS[0], child.PARAMS[1])
			if maxEffect < eff {
				beastFrameID = child.ID
				beastCount = child.PARAMS[1]
				maxEffect = eff
			}
			if minEffect > eff {
				worstFrameID = child.ID
				worstCount = child.PARAMS[1]
				minEffect = eff
			}
		}
		// продолжим рекурсивно искать в нем далее
		scanEpizRulesWords(&child, wordID)
	}
	return
}
func existsWordInPraseArr(wordID int, phraseArr []int) bool {
	for i := 0; i < len(phraseArr); i++ {
		if phraseArr[i] == -1 { // фраза не распознанна
			continue
		}
		if existsWordInPrase(wordID, phraseArr[i]) {
			return true
		}
	}
	return false
}
func existsWordInPrase(wordID int, phrase int) bool {
	wArr := word_sensor.GetWordsArrFromPraseID(phrase)
	if wArr == nil {
		return false
	}
	for i := 0; i < len(wArr); i++ {
		if wordID == wArr[i] {
			return true
		}
	}
	return false
}

// ///////////////////////////////
// найти наилучший и наихудший случай из всех слов wArr []int
type WordArrModel struct {
	maxEffect    int // наилучший эффект слова
	minEffect    int // наихудший эффект слова
	beastFrameID int // ID наилучшего по эффекту кадра
	worstFrameID int // ID наихудчшего по эффекту кадра
	beastWordID  int // ID наилучшего по эффекту кадра
	worstWordID  int // ID наихудчшего по эффекту кадра
	beastCountID int // уверенность наилучшего по эффекту кадра
	worstCountID int // уверенность наихудчшего по эффекту кадра
}

var amaxEffect = 0
var aminEffect = 0
var abeastFrameID = 0
var aworstFrameID = 0
var abeastCount = 0 // уверенность наилучшего по эффекту кадра
var aworstCount = 0 // уверенность наихудчшего по эффекту кадра
var abeastWordID = 0
var aworstWordID = 0

func getWordArrModelExactly(wArr []int) WordArrModel {
	amaxEffect = 0
	aminEffect = 0
	abeastFrameID = 0
	aworstFrameID = 0
	abeastWordID = 0
	aworstWordID = 0
	beastCount = 0
	aworstCount = 0
	for i := 0; i < len(wArr); i++ {
		wm := getWordModelExactly(wArr[i])
		if wm.beastFrameID > 0 || wm.worstFrameID > 0 { // есть значение
			if amaxEffect < wm.maxEffect {
				amaxEffect = wm.maxEffect
				beastCount = wm.beastCount
				abeastFrameID = wm.beastFrameID
				abeastWordID = wArr[i]
			}
			if aminEffect > wm.minEffect {
				aminEffect = wm.minEffect
				aworstCount = wm.worstCount
				aworstFrameID = wm.worstFrameID
				aworstWordID = wArr[i]
			}
		}
	}
	wmArr := WordArrModel{
		amaxEffect,
		aminEffect,
		abeastFrameID,
		aworstFrameID,
		abeastWordID,
		aworstWordID,
		beastCount,
		aworstCount}
	return wmArr
}

/////////////////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_problem_tree.go
____________________________________________
/* Дерево понимания проблемы (дерево нерешенных проблем problemTree)

detectedActiveLastProblemNodID - ID образа текущей проблемы в данных условиях

формат записи: ID|ParentNode|autTreeID|situationTreeID|themeID|purposeID
активирующееся с каждым новым мент.циклом, с 4 уровнями:
ID дерева автоматизмов, ID дерева ситуации, ID темы, Id цели.
К этому дереву могут привязываться мент.автоматизмы, которые теперь становятся,
по примеру моторных, просто носителями последовательности ментальных проблем
*/

package psychic

// инициализирующий блок - в порядке последовательности инициализаций
// из psychic.go
func ProblemTreeInit() {
	if EvolushnStage < 4 { // только со стадии развития 4
		return
	}
	loadProblemTree()
	if len(ProblemTree.Children) == 0 { // еще нет никаких веток
		// создать базу
		createBasicProblemTree()
	}
}

/*
	эксклюзивное хранилище информации для активации дерева проблем

Дерево понимания активируется в func infoFunc8() только когда все переменные problemTreeInfo актуальны.
*/
type prTrInfo struct {
	autTreeID       int // устанавливается в func understandingSituation()
	situationTreeID int // устанавливается в func understandingSituation()
	themeID         int // устанавливается в func runNewTheme(). Это именно ThemeImage.ID!!!
	purposeID       int // устанавливается в getMentalPurpose()
}

var problemTreeInfo prTrInfo

/*
	// узел дерева проблем

Имеет фиксированных 4 уровней (кроме базового нулевого)
формат записи: ID|ParentNode|Mood|EmotionID|SituationID
Узлы всех уровней могут произвольно меняться на другие для переактивации Дерева.

Дерево может переактивароваться при срабатывании мент.автоматизмов с действиями
MentalActionsImages.activateBaseID и MentalActionsImages.activateEmotion
в mental_automatizm_actions.go RunMentalAutomatizm(
*/
type ProblemTreeNode struct { // узел дерева проблем
	ID              int
	autTreeID       int // образ автоматизма (узел дерева автоматизмов AutomatizmTreeFromID)
	situationTreeID int // образ понимания ситуации (узел дерева понимания ситуации UnderstandingNodeFromID)
	themeID         int // образ темы
	purposeID       int // образ цели. Это - не сознательная цель, а мотивирующая потребность, дающая направленность мышлению.

	Children   []ProblemTreeNode // дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID   int               // ID родителя
	ParentNode *ProblemTreeNode  // адрес родителя
}

var ProblemTree ProblemTreeNode

// var ProblemTreeNodeFromID=make(map[int]*ProblemTreeNode)
var ProblemTreeNodeFromID []*ProblemTreeNode // сам массив
// var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteProblemTreeNodeFromID(index int, value *ProblemTreeNode) {
	addProblemTreeNodeFromID(index)
	ProblemTreeNodeFromID[index] = value
}
func addProblemTreeNodeFromID(index int) {
	if index >= len(ProblemTreeNodeFromID) {
		newSlice := make([]*ProblemTreeNode, index+1)
		copy(newSlice, ProblemTreeNodeFromID)
		ProblemTreeNodeFromID = newSlice
	}
}

// считывание члена
func ReadeProblemTreeNodeFromID(index int) (*ProblemTreeNode, bool) {
	if index >= len(ProblemTreeNodeFromID) || ProblemTreeNodeFromID[index] == nil {
		return nil, false
	}
	return ProblemTreeNodeFromID[index], true
}

////////////////////////////////////////////

// последовательность узлов активной ветки
//var ActiveBranchProblemArr []int

/*
попытка активации дерева ментальных автоматизмов
*/
var detectedActiveLastProblemNodID = 0 // ID конечного узла активной ветки
var oldDetectedActiveLastProblemNodID = 0

// нераспознанный остаток - НОВИЗНА
var CurrentProblemTreeEnd []int
var currentProblemStepCount = 0

// массив узлов активной ветки   currentProblemActivedNodes=getcurrentProblemActivedNodes(lastID)
var currentProblemActivedNodes []*ProblemTreeNode // вначале конечный узел
// последовательность узлов активной ветки
var activeBranchProblemNodeArr []int // подряд

/*
	Активация дерева понимания проблемы ProblemTreeActivation

происходит при начале нового цикла мышления TODO
Вызов из infoFunc8()
*/
func ProblemTreeActivation() {

	if EvolushnStage < 4 { // только со стадии развития 4
		return
	}
	if PulsCount < 4 { // не активировать пока все не устаканится
		return
	}

	oldDetectedActiveLastProblemNodID = detectedActiveLastProblemNodID
	detectedActiveLastProblemNodID = 0

	activeBranchProblemNodeArr = nil
	CurrentProblemTreeEnd = nil
	currentProblemStepCount = 0
	currentProblemActivedNodes = nil
	///////// если еще не были активрованы деревья автоматизмов и ситуации (при просыпании)
	if detectedActiveLastNodID == 0 {
		automatizmTreeActivation()
	}

	// 4 уровня условий в виде ID их образов
	// определить ID проблемы
	// в infoFunc8()доблжет получаться дает mentalInfoStruct.mentalPurposeID

	var lev1 = problemTreeInfo.autTreeID
	var lev2 = problemTreeInfo.situationTreeID
	var lev3 = problemTreeInfo.themeID
	var lev4 = problemTreeInfo.purposeID

	// создать массив параметров ветки дерева
	condArr := getProblemActiveConditionsArr(lev1, lev2, lev3, lev4)
	conditionProblemFound(0, condArr, &ProblemTree)

	// результат поиска:
	if detectedActiveLastProblemNodID > 0 {
		// есть ли неучтенные условия?
		conditionsCount := getConditionsCount(condArr)
		CurrentProblemTreeEnd = condArr[currentProblemStepCount:] // НОВИЗНА
		/*Алексей: detectedActiveLastProblemNodID = formingProblemBranch(detectedActiveLastProblemNodID, currentProblemStepCount+1, condArr)
		Та же ситуация, что и с деревом автоматизмов. Почему currentProblemStepCount+1? Допустим, по этому 4-х уровневому дереву дошли до 3 уровня и остался еще один, которого там нет и его надо дорастить. Если оставить currentProblemStepCount+1 то передадим в функцию доращивания вместо 3 уровня 4 и соответственно никакого доращивания не будет. Но что то ты же имел в виду...
		Уберу этот +1 - пока не вижу в нем смысла.

		Тогда и в understanding_tree.go нужно убрать currentUnderstandingStepCount+1
		В func addNewUnderstandingBranchFromNodes( вообще можно начинать растить ветку с самого нуля,т.к. если такая есть, то createNewUnderstandingNode( просто вернет ее ID.
		Убрал в understanding_tree.go +1.
		*/
		if currentProblemStepCount < conditionsCount { // не пройдено до конца имеющихся условий
			// нарастить недостающее в ветке дерева
			detectedActiveLastProblemNodID = formingProblemBranch(detectedActiveLastProblemNodID, currentProblemStepCount, condArr)
		}
	} else { // вообще нет совпадений для данных условий
		// нарастить недостающее в ветке дерева
		detectedActiveLastProblemNodID = formingProblemBranch(detectedActiveLastProblemNodID, currentProblemStepCount, condArr)
		CurrentProblemTreeEnd = condArr // все - новизна
	}
	// все узлы активной ветки, в начале - конечный узел
	currentProblemActivedNodes = getcurrentProblemActivedNodes(detectedActiveLastProblemNodID)

	mainDreamCycle := IsDreamMainProcess()
	if mainDreamCycle != nil { //процесс пассивного размышления, не в ответ на Стимул
		//lastCommonDiffValue, _ := wasChangingMoodCondition()
		//fixDreamsRules(mainDreamCycle, lastCommonDiffValue)
	}

	return
}

func conditionProblemFound(level int, cond []int, node *ProblemTreeNode) {
	if cond == nil || len(cond) == 0 {
		return
	}

	ost := cond[1:]

	for n := 0; n < len(node.Children); n++ {
		cld := node.Children[n]
		var levID = 0
		switch level {
		case 0:
			levID = cld.autTreeID
		case 1:
			levID = cld.situationTreeID
		case 2:
			levID = cld.themeID
		case 3:
			levID = cld.purposeID
		}
		if cond[1] == levID {
			detectedActiveLastProblemNodID = cld.ID
			activeBranchProblemNodeArr = append(activeBranchProblemNodeArr, cld.ID)
		} else {
			currentProblemStepCount = level - 1
			continue
		}

		level++
		currentProblemStepCount = level
		conditionProblemFound(level, ost, &node.Children[n])
		return // раз совпало, то другие ветки не смотреть
	}
	return
}

____________________________________________

BOT/brain/psychic/understanding_problem_tree_functions.go
____________________________________________
/* Функции для Дерева понимания проблемы
запись ID|ParentNode|autTreeID|situationTreeID|themeID|purposeID
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

///////////////////////////////

func getCurSituation()*SituationImage{
//	stID:=UnderstandingNodeFromID[detectedActiveLastUnderstandingNodID]
	stID,ok:=ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
	if !ok {
		return nil
	}
	node,ok:=ReadeSituationImageFromIdArr(stID.SituationID)
	if ok {
		return node
	}
	return nil
}
///////////////////////////////////////
func getCurTheme()*ThemeImage{
	//stID:=ProblemTreeNodeFromID[detectedActiveLastProblemNodID]
	stID,ok:=ReadeProblemTreeNodeFromID(detectedActiveLastProblemNodID)
	if !ok {
		return nil
	}

	node,ok:=ReadeThemeImageFromID(stID.themeID)
	if ok {
		return node
	}
	return nil
}
///////////////////////////////////////
func getCurPurpose()*PurposeImage{
	//stID:=ProblemTreeNodeFromID[detectedActiveLastProblemNodID]
	stID,ok:=ReadeProblemTreeNodeFromID(detectedActiveLastProblemNodID)
	if !ok {
		return nil
	}

	node,ok:=ReadePurposeImageFromID(stID.purposeID)
	if ok {
		return node
	}
	return nil
}
///////////////////////////////////////

/* Создать новый узел дерева понимания проблемы.
Формат записи:
ID|ParentNode|autTreeID|situationTreeID|themeID|purposeID
*/
var lastProblemTreeNodeID=0
func createNewProblemTreeNode(parent *ProblemTreeNode,id int,autTreeID int,situationTreeID int,
	themeID int,purposeID int,CheckUnicum bool)(int,*ProblemTreeNode){

	if parent == nil{
		return 0,nil
	}

/*	var flgNoAtmzAdt=false
	// пока закоментим - может и не надо это отсекать
	if atmz,ok:=AutomatizmTreeFromID[autTreeID];ok{
		if atmz.PhraseID==0 && atmz.SimbolID==0{
			if atmzAct,ok:=ActivityFromIdArr[atmz.ActivityID];ok{
				if atmzAct.ActID==nil{
					flgNoAtmzAdt=true
				}
			}else{
				flgNoAtmzAdt=true
			}
		}
	}
	if flgNoAtmzAdt{ //не зачем создавать ветку проблем, если в пусковом стимуле автоматизма нет акций и вербальных действий
		return 0,nil
	}*/

	// если есть такой узел, то не создавать
	if CheckUnicum {
		idOld,nodeOld:=FindProblemTreeNodeFromCondition(autTreeID,situationTreeID,themeID,purposeID)
		if idOld>0{
			return idOld,nodeOld
		}
	}

	if id==0{
		lastProblemTreeNodeID++
		id=lastProblemTreeNodeID
	}else{
		if lastProblemTreeNodeID<id{
			lastProblemTreeNodeID=id
		}
	}

	var node ProblemTreeNode
	node.ID = id
	node.ParentNode=parent
	node.ParentID = parent.ID

	node.autTreeID=autTreeID
	node.situationTreeID=situationTreeID
	node.themeID=themeID
	node.purposeID=purposeID

	parent.Children = append(parent.Children, node)

	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *ProblemTreeNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
		}
	}

	//ProblemTreeNodeFromID[id]=&node
	WriteProblemTreeNodeFromID(id, &node)

	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в ProblemTreeNodeFromID:
	updateProblemTreeNodeFromID(parent)// здесь потому, что при загрузке из файла нужно на лету получать адреса

	return id,newN
}
// создать первые 4 ветки базовых состояний
func createBasicProblemTree(){
	notAllowScanInTreeThisTime=true // запрет показа карты при обновлении
	ProblemTree.ID = 0
	//ProblemTreeNodeFromID[0] = &ProblemTree
	WriteProblemTreeNodeFromID(0, &ProblemTree)

	if doWritingFile {SaveProblemTree() }
	// SaveProblemTree()
	notAllowScanInTreeThisTime = false // запрет показа карты при обновлении
}
// корректируем адреса всех узлов
func updateProblemTreeNodeFromID(parent *ProblemTreeNode){
	//updatingProblemTreeNodeFromID(&VernikePhraseTree)
	updatingProblemTreeNodeFromID(parent)
}
// проход всего дерева
func updatingProblemTreeNodeFromID(rt *ProblemTreeNode){
	if rt.ID>0 {
		//rt.ParentNode=ProblemTreeNodeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		node,ok:=ReadeProblemTreeNodeFromID(rt.ParentID)
		if ok {
			rt.ParentNode=node
		}
		ProblemTreeNodeFromID[rt.ID] = rt
	}
	if rt.Children == nil{// конец ветки
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		updatingProblemTreeNodeFromID(&rt.Children[i])
	}
}

/* загрузить записанное дерево
Формат записи:
ID|ParentNode|autTreeID|situationTreeID|themeID
*/
func loadProblemTree(){

	//нулевой узел
	//[0]=&ProblemTree// все по нулям по умолчанию

	//ProblemTreeNodeFromID[0]=rt
	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/Problem_tree.txt")
	cunt:=len(strArr)
	ProblemTreeNodeFromID = make([]*ProblemTreeNode, cunt)//задать сразу имеющиеся в файле число
	WriteProblemTreeNodeFromID(0, &ProblemTree)
	//просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		if len(strArr[n])<2{
			panic("Сбой загрузки дерева проблем: ["+strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p:=strings.Split(strArr[n], "|")
		id,_:=strconv.Atoi(p[0])
		parentID,_:=strconv.Atoi(p[1])
		autTreeID,_:=strconv.Atoi(p[2]) // PsyBaseautTreeID: -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
		situationTreeID,_:=strconv.Atoi(p[3])
		themeID,_:=strconv.Atoi(p[4])
		purposeID,_:=strconv.Atoi(p[5])

		// новый узел с каждой строкой из файла
		var saveDoWritingFile= doWritingFile; doWritingFile =false
		node,ok:=ReadeProblemTreeNodeFromID(parentID)
		if !ok {
			continue
		}
		createNewProblemTreeNode(node,id,autTreeID,situationTreeID,
			themeID,purposeID,false)
		doWritingFile =saveDoWritingFile
	}
	return
}

// ID|ParentNode|autTreeID|situationTreeID|themeID
func SaveProblemTree(){
	if EvolushnStage < 4 { // только со стадии развития 4
		return
	}
	notAllowScanInTreeThisTime=true
	var out=""
	cnt:=len(ProblemTree.Children)
	for n := 0; n < cnt; n++ {// чтобы записывалось по порядку родителей
		out+=getProblemTreeNode(&ProblemTree.Children[n])
	}

	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/Problem_tree.txt",out)
	notAllowScanInTreeThisTime = false
	return
}
// такой проход чтодбы дочерние узлы шли по порядку и всегда были бы родители
func getProblemTreeNode(wt *ProblemTreeNode)(string){
	var out="" //ID|ParentNode|autTreeID|situationTreeID|themeID
	//	if wt.ParentID>0 {
	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|"
	out += strconv.Itoa(wt.autTreeID) + "|"
	out += strconv.Itoa(wt.situationTreeID) + "|"
	out += strconv.Itoa(wt.themeID) + "|"
	out += strconv.Itoa(wt.purposeID)
	out +="\r\n"
	//	}
	if(wt.Children==nil){// конец
		return out
	}
	for n := 0; n < len(wt.Children); n++ {
		out+=getProblemTreeNode(&wt.Children[n])
	}
	return out
}

// найти КОНЕЧНЫЙ узел по условиям
func FindProblemTreeNodeFromCondition(autTreeID int,situationTreeID int,
	themeID int,purposeID int)(int,*ProblemTreeNode){
	/*
	ProblemTreeNodeMapCheck()
		for k, v := range ProblemTreeNodeFromID {
		if v==nil{continue}
	ProblemTreeNodeMapCheck()
			if v.autTreeID==autTreeID && v.situationTreeID==situationTreeID &&
				v.themeID==themeID {
				return k, v
			}
		}
	*/
	var id=0
	var aut *ProblemTreeNode
	cnt:=len(ProblemTree.Children)
	for n := 0; n < cnt; n++ {
		id,aut=checkProblemTree(&ProblemTree.Children[n],autTreeID,situationTreeID,themeID,purposeID)
		if id >0{
			return id,aut
		}
	}
	return 0,nil
}

func checkProblemTree(v *ProblemTreeNode,autTreeID int,situationTreeID int,
	themeID int,purposeID int)(int,*ProblemTreeNode){
	var id=v.ID
	var aut =v

	// как только наткнется в предыдущих на такое услове - выдаст ID этой ветки
	if v.autTreeID==autTreeID && v.situationTreeID==situationTreeID && v.themeID==themeID && v.purposeID==purposeID  {
		return v.ID,v
	}

	if v.Children==nil {// конец
		return 0,nil
	}
	for n := 0; n < len(v.Children); n++ {
		id,aut=checkProblemTree(&v.Children[n],autTreeID,situationTreeID,themeID,purposeID)
		if id>0{
			return id,aut
		}
	}
	return 0,nil //v.ID

}

// выдать массив узлов ветки по конечному ID, начиная с конечного к первому
func getcurrentProblemActivedNodes(lastID int)([]*ProblemTreeNode){
	var nodws []*ProblemTreeNode
//	node:=ProblemTreeNodeFromID[lastID]
	node,ok:=ReadeProblemTreeNodeFromID(lastID)
	if ok{
		nodws=append(nodws,node)
		node=node.ParentNode
	}
	return nodws
}

/* создание иерархии АКТИВНЫХ образов контекстов условий и пусковых стимулов в виде ID образов в [4]int
создать последовательность уровней условий в виде массива  ID последовательности ID уровней
*/
func getProblemActiveConditionsArr(lev1 int, lev2 int, lev3 int, lev4 int)([]int){
	arr:=make([]int, 5)
	arr[0]=0//т.к. в conditionProblemFound сразу ost:=cond[1:] - пусть обрежет нулевой член
	arr[1]=lev1
	arr[2]=lev2
	arr[3]=lev3
	arr[4]=lev4
	return arr
}

// создание новой ветки, начиная с заданного узла
func addNewProblemBranchFromNodes(level int,cond []int,node *ProblemTreeNode)(int){
	if node==nil {
		return 0
	}
	if level>=len(cond) {
		return node.ID
	}

	if cond[2]==0 || cond[3]==0 || cond[4]==0{
		return 0
	}

	var id=0
	switch(level){
	case 0:// нет ни одонго узла
//		id,_=createNewProblemTreeNode(node,0,0,0,0,0,true)
	case 1:
		id,_=createNewProblemTreeNode(node,0,cond[1],0,0,0,true)
	case 2:
		id,_=createNewProblemTreeNode(node,0,cond[1],cond[2],0,0,true)
	case 3:
		id,_=createNewProblemTreeNode(node,0,cond[1],cond[2],cond[3],0,true)
	case 4:
		id,_=createNewProblemTreeNode(node,0,cond[1],cond[2],cond[3],cond[4],true)
	}
	level++
	node,ok:=ReadeProblemTreeNodeFromID(id)
	if !ok {
		return 0
	}
	id=addNewProblemBranchFromNodes(level,cond, node)
	return id
}

// создание ветки, начиная с заданного узла fromID
func formingProblemBranch(fromID int,lastLevel int,condArr []int)(int){
	lastNode :=&ProblemTree// нит ни одного узла, нужно сделать всю ветку
	if fromID>0{
//		lastNode = ProblemTreeNodeFromID[fromID]
		node,ok:=ReadeProblemTreeNodeFromID(fromID)
		if ok {
			lastNode=node
		}
	}

	lastNodeID:=addNewProblemBranchFromNodes(lastLevel,condArr,lastNode)
	if lastNodeID>0{
//		 SaveProblemTree() // сохранять в общем порядке, при закрытии и по времени сохранения
	}
	return lastNodeID
}

// выдать массив узлов ветки по заданному ID узла
func getBrangeProblemTreeNodeArr(lastNodeId int)([]*ProblemTreeNode){
	var nArr []*ProblemTreeNode
//	node:=ProblemTreeNodeFromID[lastNodeId]
	node,ok:=ReadeProblemTreeNodeFromID(lastNodeId)
	if !ok { return nil}
	for {
		if node==nil {
			break
		}
		nArr = append(nArr, node)
		node=node.ParentNode
	}
	return nArr
}

// выдать массив ID узлов ветки по заданному ID узла
func getBrangeProblemTreeNodeIdArr(lastNodeId int)([]int){
	var nArr []int
	//node:=ProblemTreeNodeFromID[lastNodeId]
	node,ok:=ReadeProblemTreeNodeFromID(lastNodeId)
	if !ok { return nil}
	for {
		if node==nil {
			break
		}
		nArr = append(nArr, node.ID)
		node=node.ParentNode
	}
	return nArr
}

// для Пульта - инфа о ветке Дерева
func ShowProblemTreeInfo(pID int)string{
	if pID == 0 {
		return "Нулевой ID..."
	}
	if len(ProblemTree.Children)==0 { // еще нет никаких веток
		return "Еще нет Дерева проблем"
	}
//	node:=ProblemTreeNodeFromID[pID]
	node,ok:=ReadeProblemTreeNodeFromID(pID)
	if !ok{
		return "Нет ветки дерева с ID="+strconv.Itoa(pID)
	}
	out:="<b>Ветка дерева проблем с ID="+strconv.Itoa(pID)+"</b><br>"
	out+="ID дерева автоматизмов: <span onclick='show_atmzm_tree("+strconv.Itoa(node.autTreeID)+")'><b>"+strconv.Itoa(node.autTreeID)+"</b><span>"
	out+="ID дерева ситуации: <span onclick='show_unde_tree("+strconv.Itoa(node.situationTreeID)+")'><b>"+strconv.Itoa(node.situationTreeID)+"</b><span>"
	out+="ID Темы мышления: <span  onclick='get_situation("+strconv.Itoa(node.themeID)+")'><b>"+strconv.Itoa(node.themeID)+"</b><span>"
	out+="ID Цели мышления: <span onclick='get_purpose("+strconv.Itoa(node.purposeID)+")'><b>"+strconv.Itoa(node.purposeID)+"</b><span>"

	return out
}
____________________________________________

BOT/brain/psychic/understanding_problem_tree_pult.go
____________________________________________
/* Вывод дерева проблем на пульт
 */

package psychic

import (
	"strconv"
)

// образ дерева автоматизмов для вывода
var problemTreeStr=""

func GetMentalPriblemTreeForPult(limit int)string{
	problemTreeStr=""
	// против паники типа "одновременная запись и считывание карты"
	if notAllowScanInTreeThisTime{
		return "!Временно запрещена работа func GetAutomatizmTreeForPult() т.к. идет параллельная обработка."
	}
	if len(ProblemTree.Children)==0 { // еще нет никаких веток
		return "Еще нет Дерева проблем"
	}

	scanMentalPriblemNodes(-1, &ProblemTree)

	if len(problemTreeStr)<10{
		return "Еще нет информационных веток дерева проблем"
	}
	return problemTreeStr
}

// ID|ParentNode|autTreeID|situationTreeID|themeID|purposeID
func scanMentalPriblemNodes(level int,node *ProblemTreeNode) {
	if node.ID > 0{
		out:="<span style='color:#666666;'>"+strconv.Itoa(node.ID)+":</span> "

		out += setOutShift(level)

		switch level {
		case 0:// ID Дерева автоматизмов
			out +=getAutomTreeNodeString(node.autTreeID)
		case 1: // ID ситуации
		if node.situationTreeID==0{
			return
		}
			out +=getSituationString(node.situationTreeID)
		case 2: // ID Темы
			if node.themeID==0{
				return
			}
			out +=getThemeStr(node.themeID)
		case 3: // ID Цели
			if node.purposeID==0{
				return
			}
			out +=getPurposeString(node.purposeID)
			/* // привязанный мент.автоматизм
			ma:=ExistsStaffMentAutomatizmForThisNodeID(node.ID)
			if ma!=nil {
				out += " Мент.автоматизм c ID=<b> <span style='cursor:pointer;color:blue' onClick='get_ment_automatizm(" + strconv.Itoa(ma.ID) + ")'></b>" + strconv.Itoa(ma.ID) + "</span>" + "\n"
			}else{out += " Мент.авт-м не привязан."}
			*/
		}
		out +="<br>\n"
		problemTreeStr +=out
	}
	level++
	for n := 0; n < len(node.Children); n++ {

		scanMentalPriblemNodes(level, &node.Children[n])
	}
}

func getAutomTreeNodeString(id int)string{

//	atmzm:=AutomatizmTreeFromID[id]
	atmzm,ok:=ReadeAutomatizmTreeFromID(id)
	if !ok {
		return ""
	}
	out:=""
	if atmzm == nil {
		out += "<span style='color:red'>Нет узла дерева автоматизма с ID=" + strconv.Itoa(id)+"</span>"
	} else {
		out += "ID дерева автоматизмов: <b> <span style='cursor:pointer;color:blue' onClick='show_node_automatizms(" + strconv.Itoa(id) + ")'>" + strconv.Itoa(id) + "</span>" + "</b>"
	}
	return out

}

func getThemeStr(id int)string{

//	theme:=ThemeImageFromID[id]
	out:=""
	theme,ok:=ReadeThemeImageFromID(id)
	if !ok {
		out += "<span style='color:red'>Нет Темы с ID=" + strconv.Itoa(id)+"</span>"
	} else {
		out += "Тема: <b> <span style='cursor:pointer;color:blue' onClick='get_theme(" + strconv.Itoa(theme.ID) + ")'>" + strconv.Itoa(id) + "</span>" + "</b>"
	}
	return out
}

// PurposeID
func getPurposeString(id int)string{
//	pp:=PurposeImageFromID[id]
	out:=""
	_,ok:=ReadePurposeImageFromID(id)
	if !ok {
		out += "<span style='color:red'>Нет образа цели с func getPurposeString id=" + strconv.Itoa(id)+"</span>"
	} else {
		out += "Цель: <b> <span style='cursor:pointer;color:blue' onClick='get_purpose(" + strconv.Itoa(id) + ")'>" + strconv.Itoa(id) + "</span>" + "</b>"
	}
	return out
}

func getPurposeDetaileString(id int)string{
	if id==0{
		return "<span style='color:red'>Нет образа цели.</span>"
	}
//	pp:=PurposeImageFromID[id]
	out:=""
	_,ok:=ReadePurposeImageFromID(id)
	if ok {
		out += "Цель: <b> " + GetMentalPurposeForNodeInfo(id) + "</b>"
	} else {
		out += "<span style='color:red'>Нет образа цели с ID=" + strconv.Itoa(id)+"</span>"
	}
	return out
}
____________________________________________

BOT/brain/psychic/understanding_process.go
____________________________________________
/* Процессы осмысления: создание и использование ментальных автоматизмов
для Дерева понимания (или дерева ментальных автоматизмов)

*/

package psychic

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
)

/*
	детекция ленивого состояния - по актуальности реагирования на услоивня.

Учитывает Доминанту, но не учитывает рвущегося на выполнение автоматизма.
*/
func isIdleness() bool {
	if CurrentInformationEnvironment.veryActualSituation || // не актуальная ситуация
		CurrentInformationEnvironment.danger || // нет опасности
		mentalInfoStruct.mentalPurposeID == 0 || // нет текущей поставленной Цели
		CurrentProblemDominanta != nil || // нет актуальной Доминанты
		(InterruptMemory == nil || len(InterruptMemory) == 0) { // нет прерываний размышлений к которым нужно вернуться
		return false
	}
	return true
}

// /////////////////////////////////////////////////////
// обработка структур в свободном состоянии, в первую очередь - эпизодической памяти
func processingFreeState(stopMentalWork bool) {

	if stopMentalWork { //- прекратить обработку
		return
	}

	// TODO переработка происходившего (эпизодическая память обрабатывается в GotoDreaming(true)
	//EpisodeMemoryLastCalcID - последний эпизод, который был осмыслен в лени или во сне
}

/*
	после периода ожидания:

Учесть последствия текущего ментального цикла,
выявить Правила и записать в эпизод.пямять.
Сразу после обработки периода ожидания запускается дерево понимания и объявный запуск consciousnessElementary(1,0)
так что никаких действий совершать в afterWaitingPeriod() или в самой afterWaitingPeriod() не следует.

effect =lastCommonDiffValue
*/
func afterWaitingPeriod(effect int) {

	/* Определить соотвествие желаемому и получаемому == определение степени достижения Цели PurposeImage,
	   в том числе для ментальных действий, а не только при объективных.
	      оценить совокупный эффект по нескольким атрибутам: объективному эффекту + достижение Цели + изменению значимостей
	*/
	effectValuation := getMentalEffect(effect) // детектор достижения Цели
	// записать в эпизод.пямять ментальный кадр. Запись saveNewEpisodic всегда предшествует: lastEpisodicMemID.
	saveNewMentalEpisodic(effectValuation) // здесь в конце обнуляется effectValuation

	// осмыслить результат, задав тему ThemeImage.Type=2
	if effectValuation < 0 { // негативный эффект
		// -effectValuation обычно большой и уже не перекрывается другой темой, так что задаем фиксированный 2
		runNewTheme(2, 2) //2 "Негативный эффект ментального автоматизма"
	}

}

///////////////////////////////////////////////////////

/*
Оценка суммарного ментального эффекта в период ожидания
*/
func getMentalEffect(effect0 int) int {
	/* улучшилось ли положение с учетом текущего PurposeImage 4-го узла ветки понимания?
	currentUnderstandingActivedNodes[]*UnderstandingNode // начиная с конечного к первому
	*/
	effectPurpose := getMentalPurposeEffect()

	// улучшилась ли значимость объекта внимания extremImportanceObject
	effectA := 0
	if extremImportanceObject != nil {
		oldVal := extremImportanceObject.extremVal
		eObj, newVal := getObjectsImportanceValue(extremImportanceObject.objID, detectedActiveLastProblemNodID)
		if eObj != nil {
			if newVal > oldVal { // улучшилось
				effectA = 1
			}
			if newVal < oldVal { // ухудшилось
				effectA = -1
			}
		}
	}

	// улучшилась ли значимость субъекта внимания extremImportanceMentalObject
	effectMA := 0
	if extremImportanceMentalObject != nil {
		oldVal := extremImportanceMentalObject.extremVal
		eObj, newVal := getObjectsImportanceValue(extremImportanceMentalObject.objID, detectedActiveLastProblemNodID)
		if eObj != nil { // если не нашел объект, это не значит что стало лучше/хуже
			if newVal > oldVal { // улучшилось
				effectMA = 1
			}
			if newVal < oldVal { // ухудшилось
				effectMA = -1
			}
		}
	}

	// Коэффициенты эффектов разного вида должны сильно влиять не ментальность твари: что для нее важнее.
	effectValuation := 0
	if EvolushnStage < 5 {
		effectValuation = effect0*3 + effectPurpose*1 + effectA*1 + effectMA*0
	} else { // повышенная роль заданной цели effect4 и объекта внимания
		effectValuation = effect0*2 + effectPurpose*3 + effectA*3 + effectMA*2
	}
	if effectValuation != 0 { // иначе при эффекте 0 выдаст -1 и потом заблокирует автоматизм
		if effectValuation > 1 {
			effectValuation = 1
		}
		if effectValuation < 1 {
			effectValuation = -1
		}
	}

	return effectValuation
}

/////////////////////////////////////////////////

/*
	Оценить эффект в период ожидания по ментальной цели ОБЪЕКТИВНО: после активации деревьев

Эффект от 0 до 10.
Эффект - в том, насколько достигнуто целевое состояние.
Цель считается достигнутой (эффект совершенных ментальных действий) если
заданные (НЕНУЛЕВЫЕ) параметры достигнуты.
При нескольких заданных параметрах цель может быть частично достигнута.
Эффект прикидывается в зависимости от полноты достижения цели:
при полном недостижении эффект ==0 при полном достижении +10
*/
var PsyBaseMoodOld = -10
var EmotionReceptionOld = 0
var SituationIDOld = 0

func getMentalPurposeEffect() int {
	if mentalInfoStruct.mentalPurposeID == 0 { //текущая цель не была задана
		return 0
	}
	var effect = 0
	var allPurposeHit = true // все цели достигнуты
	//	mp:=PurposeImageFromID[mentalInfoStruct.mentalPurposeID]
	mp, ok := ReadePurposeImageFromID(mentalInfoStruct.mentalPurposeID)
	if !ok {
		return 0
	}

	// настроение
	// mp.moodeID всегда считаться заданным в getMentalPurposeEffect().
	if mp.target == 1 { // добиться повторения
		if mp.moodeID == PsyBaseMood {
			effect += 3
		} else {
			allPurposeHit = false
		}
	}
	if mp.target == 2 { // добиться улучшения
		if PsyBaseMoodOld > PsyBaseMood {
			effect += 3
		} else {
			allPurposeHit = false
		}
	}
	PsyBaseMoodOld = PsyBaseMood
	////////////////////////////
	if mp.target == 1 { // добиться повторения
		if mp.emotonID > 0 {
			if mp.emotonID == CurrentEmotionReception.ID {
				effect += 3
			} else {
				allPurposeHit = false
			}
		}
	}
	///////////
	if mp.target == 2 { // добиться улучшения
		if mp.situationID > 0 {
			if isEmotonBetter(EmotionReceptionOld, CurrentEmotionReception.ID) {
				effect += 3
			} else {
				allPurposeHit = false
			}
		}
	}
	EmotionReceptionOld = CurrentEmotionReception.ID
	//////////////////////////////////
	//	var curSituationID=UnderstandingNodeFromID[detectedActiveLastUnderstandingNodID].SituationID
	unode, ok := ReadeUnderstandingNodeFromID(detectedActiveLastUnderstandingNodID)
	if !ok {
		return 0
	}
	var curSituationID = unode.SituationID
	if mp.target == 1 { // добиться повторения
		if mp.emotonID > 0 {
			if mp.situationID == curSituationID {
				effect += 3
			} else {
				allPurposeHit = false
			}
		}
	}
	///////////
	if mp.target == 2 { // добиться улучшения
		if mp.situationID > 0 {
			if isSituationBetter(SituationIDOld, curSituationID) {
				effect += 3
			} else {
				allPurposeHit = false
			}
		}
	}
	SituationIDOld = mentalInfoStruct.SituationID

	if effect > 10 {
		effect = 10
	}
	if allPurposeHit {
		return 10
	}
	return effect
}

////////////////////////////////////
/* Цель - улучшить эмоцию - имеется в виду, что
сумма весов позитивных эмоциональных контекстов превышает сумму весов негатиных.
Выбором весов и нагативаности определяется характер твари.
*/
func isEmotonBetter(oldID int, curID int) bool {
	if oldID == 0 || curID == 0 {
		return false
	}
	emOld, ok1 := EmotionFromIdArr[oldID]
	emCur, ok2 := EmotionFromIdArr[curID]
	if !ok1 || !ok2 {
		return false
	}
	// массивы активных контекстов
	aOld := emOld.BaseIDarr
	aCur := emCur.BaseIDarr
	var wArr = gomeostas.BaseContextWeight

	// старая сумма весов контекстов
	var wOld = 0 //позитивных и нешативных
	for i := 0; i < len(aOld); i++ {
		switch aOld[i] {
		case 1: //Пищевой	- Пищевое поведение, восполнение энергии, на что тратится время и тормозятся антагонистические стили поведения.
			wOld -= wArr[1]
		case 2: //	Поиск	- Поисковое поведение, любопытство. Обследование объекта внимания, поиск новых возможностей.
			wOld += wArr[1]
		case 3: //	Игра	- Игровое поведение - отработка опыта в облегченных ситуациях или при обучении.
			wOld += wArr[1]
		case 4: //	Гон	- Половое поведение. Тормозятся антагонистические стили
			wOld += wArr[1]
		case 5: //	Защита	- Оборонительные поведение для явных признаков угрозы или плохом состоянии.
			wOld -= wArr[1]
		case 6: //	Лень	- Апатия в благополучном или безысходном состоянии.
			wOld -= wArr[1]
		case 7: //	Ступор	- Оцепенелость при непреодолимой опастbase_context_ativnostности или когда нет мотивации при благополучии или отсуствии любых возможностей для активного поведения.
			wOld -= wArr[1]
		case 8: //	Страх	- Осторожность при признаках опасной ситуации.
			wOld -= wArr[1]
		case 9: //	Агрессия	- Агрессивное поведение для признаков легкой добычи или защиты (иногда - при плохом состоянии).
			wOld += wArr[1]
		case 10: //	Злость	- Безжалостность в случае низкой оценки .
			wOld -= wArr[1]
		case 11: //	Доброта	- Альтруистическое поведение.
			wOld += wArr[1]
		case 12: //	Сон - Состояние сна. Освобождение стрессового состояния. Реконструкция необработанной информации.
		}
	}

	var wCur = 0 //позитивных и негативных
	for i := 0; i < len(aCur); i++ {
		switch aOld[i] {
		case 1: //Пищевой	- Пищевое поведение, восполнение энергии, на что тратится время и тормозятся антагонистические стили поведения.
			wCur -= wArr[1]
		case 2: //	Поиск	- Поисковое поведение, любопытство. Обследование объекта внимания, поиск новых возможностей.
			wCur += wArr[1]
		case 3: //	Игра	- Игровое поведение - отработка опыта в облегченных ситуациях или при обучении.
			wCur += wArr[1]
		case 4: //	Гон	- Половое поведение. Тормозятся антагонистические стили
			wCur += wArr[1]
		case 5: //	Защита	- Оборонительные поведение для явных признаков угрозы или плохом состоянии.
			wCur -= wArr[1]
		case 6: //	Лень	- Апатия в благополучном или безысходном состоянии.
			wCur -= wArr[1]
		case 7: //	Ступор	- Оцепенелость при непреодолимой опастbase_context_ativnostности или когда нет мотивации при благополучии или отсуствии любых возможностей для активного поведения.
			wCur -= wArr[1]
		case 8: //	Страх	- Осторожность при признаках опасной ситуации.
			wCur -= wArr[1]
		case 9: //	Агрессия	- Агрессивное поведение для признаков легкой добычи или защиты (иногда - при плохом состоянии).
			wCur += wArr[1]
		case 10: //	Злость	- Безжалостность в случае низкой оценки .
			wCur -= wArr[1]
		case 11: //	Доброта	- Альтруистическое поведение.
			wCur += wArr[1]
		case 12: //	Сон - Состояние сна. Освобождение стрессового состояния. Реконструкция необработанной информации.
		}
	}
	// сравнение эмоций на позитивность
	if wCur > wOld {
		return true
	}
	return false
}

////////////////////////////////////

/*
сравнение старой и новой ситуации
*/
func isSituationBetter(oldID int, curID int) bool {
	if oldID == 0 || curID == 0 {
		return false
	}
	//sOld,ok1:=SituationImageFromIdArr[oldID]
	sOld, ok := ReadeSituationImageFromIdArr(oldID)
	if !ok {
		return false
	}

	//sCur,ok2:=SituationImageFromIdArr[curID]
	sCur, ok2 := ReadeSituationImageFromIdArr(curID)
	if !ok2 {
		return false
	}
	/////////////////////////////////
	var wOld = 0 //позитивных и негативных
	switch sOld.SituationType {
	case 4:
		wOld += 5 //все спокойно, можно экспериментивароть
	case 5:
		wOld += -5 //оператор не прореагировал на действия в течение периода ожидания - игнорирует
		// оператор выбрал настроение
	case 11:
		wOld += 10 //Хорошее
	case 12:
		wOld += -8 //Плохое
	case 13:
		wOld += 6 //Игровое
	case 14:
		wOld += 3 //Поучить
	case 15:
		wOld += 2 //Агрессивное
	case 16:
		wOld += -5 //Защитное
	case 17:
		wOld += -6 //Протест
		//оператор нажал кнопку
	case 21:
		wOld += -3 //Непонятно
	case 22:
		wOld += 3 //Понятно
	case 23:
		wOld += -8 //Наказать
	case 24:
		wOld += 8 //Поощрить
	case 25:
		wOld += 6 //Накормить
	case 26:
		wOld += 4 //Успокоить
	case 27:
		wOld += 5 //Поиграть
	case 28:
		wOld += 5 //Предложить поучить
	case 29:
		wOld += -4 //Игнорировать
	case 30:
		wOld += -4 //Сделать больно
	case 31:
		wOld += 5 //Сделать приятно
	case 32:
		wOld += -2 //Заплакать
	case 33:
		wOld += 2 //Засмеяться
	case 34:
		wOld += 3 //Обрадоваться
	case 35:
		wOld += -1 //Испугаться
	case 36:
		wOld += 3 //Простить
	case 37:
		wOld += 7 //Вылечить
	}
	//////////////////////////////////
	var wCur = 0 //позитивных и негативных
	switch sCur.SituationType {
	case 4:
		wCur += 5 //все спокойно, можно экспериментивароть
	case 5:
		wCur += -5 //оператор не прореагировал на действия в течение периода ожидания - игнорирует
		// оператор выбрал настроение
	case 11:
		wCur += 10 //Хорошее
	case 12:
		wCur += -8 //Плохое
	case 13:
		wCur += 6 //Игровое
	case 14:
		wCur += 3 //Поучить
	case 15:
		wCur += 2 //Агрессивное
	case 16:
		wCur += -5 //Защитное
	case 17:
		wCur += -6 //Протест
		//оператор нажал кнопку
	case 21:
		wCur += -3 //Непонятно
	case 22:
		wCur += 3 //Понятно
	case 23:
		wCur += -8 //Наказать
	case 24:
		wCur += 8 //Поощрить
	case 25:
		wCur += 6 //Накормить
	case 26:
		wCur += 4 //Успокоить
	case 27:
		wCur += 5 //Поиграть
	case 28:
		wCur += 5 //Предложить поучить
	case 29:
		wCur += -4 //Игнорировать
	case 30:
		wCur += -4 //Сделать больно
	case 31:
		wCur += 5 //Сделать приятно
	case 32:
		wCur += -2 //Заплакать
	case 33:
		wCur += 2 //Засмеяться
	case 34:
		wCur += 3 //Обрадоваться
	case 35:
		wCur += -1 //Испугаться
	case 36:
		wCur += 3 //Простить
	case 37:
		wCur += 7 //Вылечить
	}

	/////////////////////////////////
	// сравнение ситуаций на позитивность
	if wCur > wOld {
		return true
	}
	return false
}

//////////////////////////////////////////////////////////////////

// есть ли данные Базовые контексты в состоаве эмоции: true - да, есть.
func existsBaseContextFromEmotionID(contextsArr []int, emotionId int) bool {
	for _, v := range EmotionFromIdArr[emotionId].BaseIDarr {
		if lib.ExistsValInArr(contextsArr, v) {
			return true
		}
	}
	return false
}

//////////////////////////////////////////////

/*
Подавление мешающих стимулов при серьезном поиске решений проблемы.
Это обеспечивает "тишину мыслей" (fornit.ru/17954)
Нужно приглушать 1 и 2-й уровни осмысления чтобы не мешали,
а вместо автоматизмов выдавать действие "игнорировать" и тем самым блокировать рефлексы.
Это состояние погруженности нужно снимать при серьезных стимулах высокой значяимости?
а так же при конце атаса, наступления лени и дремы - просто установкой флага:
isRepressionStimulsNoise=false

isRepressionStimulsNoise действует при при активации дерева автоматизмов, до активации ветки
и блокирует весь процесс активации, так же нет прерываний на более важный стимул
и нет решения доминанты по аналогии.
*/
var isRepressionStimulsNoise = false

func repressionStimulsNoises() {
	if EvolushnStage < 5 {
		return
	}
	// есть ли проблемный объект с отрицательным эффектом - основа мук творчества
	if problemExtremImportanceObject != nil {
		isRepressionStimulsNoise = true
	}
}

// контроль удержания режима isRepressionStimulsNoise
func checkRepressionStimulsNoises() {
	if EvolushnStage < 5 {
		return
	}
	if !isRepressionStimulsNoise {
		return
	}
	if IsSleeping || isIdleness() {
		isRepressionStimulsNoise = false
	}

}

// сторожевая функция определения критического состояния гомеостаза
func watchdogFunctionGomeo() {
	if !allowWatchdogFunction() {
		return
	}
	CurrentInformationEnvironment.danger = GetAttentionDanger()
	if CurrentInformationEnvironment.danger {
		isRepressionStimulsNoise = false
		return
	}
	CurrentInformationEnvironment.veryActualSituation, _ = gomeostas.FindTargetGomeostazID()
	if CurrentInformationEnvironment.veryActualSituation {
		isRepressionStimulsNoise = false
		return
	}
	painValue, _ = gomeostas.GetCurPainJoy()
	if painValue > 5 {
		isRepressionStimulsNoise = false
		return
	}
}

// сторожевая функция определения очень важного объекта внимания
func watchdogFunctionStimul(curActiveActions *ActionsImage) {
	if !allowWatchdogFunction() {
		return
	}
	eobj := getExtremObjFromID(curActiveActions.ID)
	if eobj == nil {
		return
	}
	if eobj.extremVal < -5 {

		/* TODO при появлении атасного стимула записать Правило в эпиз.память ??
		 наверное не стоит, т.к. тогда всегда будет негатив при задумчивости...
		НО если оператор наказал, то для этих условий правило уместно
		*/

		isRepressionStimulsNoise = false
		return
	}
}

func allowWatchdogFunction() bool {
	if EvolushnStage < 5 {
		return false
	}
	if !isRepressionStimulsNoise {
		return false
	}
	return true
}

///////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_purpose_image.go
____________________________________________
/* Это - осознаваемая, но не осмысливаемая цель (не поставленная осмысленно), а мотивирующая потребность, дающая направленность мышлению.
Формируется на основе актуальной Темы размышления, возникающей при некоторых событиях.
Но может быть выбрана и произвольно.

Для осмысливаемых целей - Доминанта и там надо будет более детально прописывать желаемое.
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

///////////////////////////////


/* образ цели действия.

Осознаваемая Цель - проблема Доминанты.
Отражает состояние 3-х уровней дерева ситуации:
1-MoodID, 2-EmotionID, 3-situationImageID (здесь определена текущая цель problemTreeInfo.purposeID)
Если есть доминанта, то в ней такая цель зафиксирована в виде problemTreeID (из текущей problemTreeInfo.purposeID)
Так что улучшение ситуации без доминанты подразумевает решение текущей проблемы,
а с доминантой - с той проблемой, которая бала зафиксирована в виде problemTreeID.

Дополнительно - целями мошут быть:
добиться возможности совершить данное действие и
добиться достижения эффекта моторного Правила с rules.ID

mentalInfoStruct.mentalPurposeID - поставлена текущая цель

Цель считается достигнутой (эффект совершенных ментальных действий) если
заданные (НЕНУЛЕВЫЕ) параметры достигнуты.
При нескольких заданных параметрах цель может быть частично достигнута.
Эффект прикидывается в зависимости от полноты достижения цели:
при полном недостижении эффект ==0 при полном достижении +10
*/
type PurposeImage struct {
	ID int
	// цель - добиться target
	target int// 1- повторения, 2 - улучшения
	//добиться target значения PsyBaseMood -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
	moodeID int // этот параметр будет всегда считаться заданным в getMentalPurposeEffect().

	/* В случае Цели - улучшить эмоцию (target==2) - имеется в виду, что
	сумма весов позитивных эмоциональных контекстов превышает сумму вемов негатиных:
	func isEmotonBetter(oldID int, curID int)
	 */
	emotonID int// добиться target такой эмоции

	/* В случае Цели - улучшить ситуацию - func isSituationBetter(oldID int, curID int)

	 */
	situationID int// добиться PurposeImage.target данной ситуации SituationImage

}
//var PurposeImageFromID=make(map[int]*PurposeImage)
var PurposeImageFromID []*PurposeImage  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WritePurposeImageFromID(index int, value *PurposeImage) {
	addPurposeImageFromID(index)
	PurposeImageFromID[index] = value
}
func addPurposeImageFromID(index int){
	if index >= len(PurposeImageFromID) {
		newSlice := make([]*PurposeImage, index+1)
		copy(newSlice, PurposeImageFromID)
		PurposeImageFromID = newSlice
	}
}
// считывание члена
func ReadePurposeImageFromID(index int) (*PurposeImage,bool){
	if index >= len(PurposeImageFromID) || PurposeImageFromID[index]==nil {
		return nil,false
	}
	return PurposeImageFromID[index],true
}
///////////////////////////////////////////////////////////////////


// создать новый образ желаемой цели, если такого еще нет
var lastPurposeImagePurposeID=0
func createPurposeImageID(id int,target int,moodeID int,emotonID int,situationID int,CheckUnicum bool)(int,*PurposeImage){
	if CheckUnicum {
		oldID,oldVal:=checkUnicumPurposeImage(target,moodeID,emotonID,situationID)
		if oldVal!=nil{
			return oldID,oldVal
		}
	}

	if id==0{
		lastPurposeImagePurposeID++
		id=lastPurposeImagePurposeID
	}else{
		//		newW.ID=id
		if lastPurposeImagePurposeID<id{
			lastPurposeImagePurposeID=id
		}
	}

	var node PurposeImage
	node.ID = id
	node.target = target
	node.moodeID=moodeID
	node.emotonID = emotonID
	node.situationID = situationID

//	PurposeImageFromID[id]=&node
	WritePurposeImageFromID(id, &node)

	if doWritingFile {SavePurposeImageFromIdArr() }

	return id,&node
}
func checkUnicumPurposeImage(target int,moodeID int,emotonID int,situationID int)(int,*PurposeImage){
	for id, v := range PurposeImageFromID {
		if v==nil{continue}
		if target!=v.target || moodeID!=v.moodeID || emotonID!=v.emotonID || situationID != v.situationID {
			continue
		}
		return id,v
	}
	return 0,nil
}
/////////////////////////////////////////



/////////////////////////////////////////
// сохранить образы
func SavePurposeImageFromIdArr(){
	var out=""
	for k, v := range PurposeImageFromID {
		if v==nil{continue}
		out+=strconv.Itoa(k)+"|"
		out+=strconv.Itoa(v.target)+"|"
		out+=strconv.Itoa(v.moodeID)+"|"
		out+=strconv.Itoa(v.emotonID)+"|"
		out+=strconv.Itoa(v.situationID)
		out+="\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/purpose_images.txt",out)

}
////////////////////  загрузить образы 
func loadPurposeImageFromIdArr(){

	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/purpose_images.txt")
	cunt:=len(strArr)
	//PurposeImageFromID=make(map[int]*PurposeImage)
	PurposeImageFromID = make([]*PurposeImage, cunt)//задать сразу имеющиеся в файле число
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		p:=strings.Split(strArr[n], "|")
		id,_:=strconv.Atoi(p[0])
		target,_:=strconv.Atoi(p[1])
		moodeID,_:=strconv.Atoi(p[2])
		emotonID,_:=strconv.Atoi(p[3])
		situationID,_:=strconv.Atoi(p[4])

var saveDoWritingFile= doWritingFile; doWritingFile =false
		createPurposeImageID(id,target,moodeID,emotonID,situationID,false)
doWritingFile =saveDoWritingFile
	}
	return

}
//////////////////////////////




____________________________________________

BOT/brain/psychic/understanding_situation_image.go
____________________________________________
/* Образ текущей ситуации для осмысления
4- уровень Дерева Ситуации
Здесь активная ID дерева автоматизмов (доступны моторные автоматизмы от дерева автоматизмов)

*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/*
	Образы текущей ситуации сохраняются в памяти

т.к. представляют собой 4- уровень Дерева Понимания Ситуации
*/
type SituationImage struct {
	ID int
	/* позволяет получить доступ к конечному узлу ветки дерева моторных автоматизмов и получить все инфу ее узлов
	   Здесь - вся конкретика ситуации.
	*/
	autmzmTreeNodeID int

	/* Смысловой контекст ситуации, то, на что в первую очередь обращается внимание (приоритет):
	   	1 - было ответное действие (смотреть в штатном автоматизме ветки Usefulness int - (БЕС)ПОЛЕЗНОСТЬ: вред: -10 0 +10 +n польза diffPsyBaseMood )
	   	2 - был запуск автоматизма ветки.
	   	3 - ничего не делали, но нужно осмысление
	   	4 - все спокойно, можно экспериментивароть
	   	5 - оператор не прореагировал на действия в течение периода ожидания - игнорирует? нужно достучаться?

	   	11-17 - оператор выбрал настроение (14 - Учитель при отправке или нажал кнопку Поучить)
	   	21-37 - оператор нажал кнопку (27 - Игровое при отправке или нажал кнопку Поиграть)
	   	... и т.п.

	      ?Раз в цели PurposeImage есть параметр ситуации, нужно бы иметь возможность вводить дополнительные,
	   произвольные значения SituationType? Типа как произвольность эмоций
	   - переактивировать дерево с новой ситуацией getSituationTypeArrID() infoFunc14()?

	   !!! ПРИ ДОБАВЛЕНИИ, УДАЛЕНИИИ допустимых ID SituationType НУЖНО СКОРРЕКТИРОВАТЬ getSituationTypeArrID() !!!!
	*/
	SituationType int
}

// var SituationImageFromIdArr=make(map[int]*SituationImage)
var SituationImageFromIdArr []*SituationImage // сам массив
// var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteSituationImageFromIdArr(index int, value *SituationImage) {
	addSituationImageFromIdArr(index)
	SituationImageFromIdArr[index] = value
}
func addSituationImageFromIdArr(index int) {
	if index >= len(SituationImageFromIdArr) {
		newSlice := make([]*SituationImage, index+1)
		copy(newSlice, SituationImageFromIdArr)
		SituationImageFromIdArr = newSlice
	}
}

// считывание члена
func ReadeSituationImageFromIdArr(index int) (*SituationImage, bool) {
	if index >= len(SituationImageFromIdArr) || SituationImageFromIdArr[index] == nil {
		return nil, false
	}
	return SituationImageFromIdArr[index], true
}

///////////////////////////////////////

/* при активации дерева ситуации текущая ситуация
UnderstandingNodeFromID[detectedActiveLastUnderstandingNodID].SituationID
и записывается в
curBaseStateImage.SituationID

*/

/*
	для произвольной переактивации дерева случайным значение ситуации

вводится вручную при изменении возможных ID SituationType
*/
func getSituationTypeArrID() []int {
	var arr = []int{1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27}
	var arrOut []int
	// проверяем, что такой образ уже есть
	for _, val := range arr {
		_, ok := ReadeSituationImageFromIdArr(val)
		if ok {
			arrOut = append(arrOut, val)
		}
	}
	return arrOut
}

var lastSituationImageID = 0

func createSituationImage(id int, autmzmTreeNodeID int, SituationType int, CheckUnicum bool) (int, *SituationImage) {
	if EvolushnStage < 4 { // только со стадии развития 4
		return 0, nil
	}
	if CheckUnicum {
		oldID, oldVal := checkUnicumSituationImage(autmzmTreeNodeID, SituationType)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastSituationImageID++
		id = lastSituationImageID
	} else {
		//		newW.ID=id
		if lastSituationImageID < id {
			lastSituationImageID = id
		}
	}

	var node SituationImage
	node.ID = id
	node.autmzmTreeNodeID = autmzmTreeNodeID
	node.SituationType = SituationType

	//SituationImageFromIdArr[id]=&node
	WriteSituationImageFromIdArr(id, &node)

	if doWritingFile {
		SaveSituationImage()
	}

	return id, &node
}

// ///////////////////////////////////
func checkUnicumSituationImage(autmzmTreeNodeID int, SituationType int) (int, *SituationImage) {

	for id, v := range SituationImageFromIdArr {
		if v == nil {
			continue
		}
		if autmzmTreeNodeID != v.autmzmTreeNodeID && SituationType != v.SituationType {
			continue
		}
		return id, v
	}

	return 0, nil
}

////////////////////////////////////

func SaveSituationImage() {
	var out = ""
	for k, v := range SituationImageFromIdArr {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		out += strconv.Itoa(v.autmzmTreeNodeID) + "|"
		out += strconv.Itoa(v.SituationType) + "|"
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/situation_images.txt", out)
}

//////////////////////////////////////////

func loadSituationImage() {

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/situation_images.txt")
	cunt := len(strArr)
	//SituationImageFromIdArr=make(map[int]*SituationImage)
	SituationImageFromIdArr = make([]*SituationImage, cunt) //задать сразу имеющиеся в файле число
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		autmzmTreeNodeID, _ := strconv.Atoi(p[1])
		SituationType, _ := strconv.Atoi(p[2])

		var saveDoWritingFile = doWritingFile
		doWritingFile = false
		createSituationImage(id, autmzmTreeNodeID, SituationType, false)
		doWritingFile = saveDoWritingFile
	}
	return

}

//////////////////////////////

//////////// ПРИОРИТЕТЫ СИТУАЦИЙ
/* приоритет в зависимости от ID параметра.
prioritetOfPultButtonActions имеет преимущество перед prioritetOfPultMoodActions
Это - наследственное пердопределение.
*/
// 0-7   настроение оператора
func getPrioritetOfPultMoodActions(moodID int) int {
	switch moodID {
	case 1:
		return 1 //Хорошее
	case 6:
		return 2 //Защитное
	case 7:
		return 3 //Протест
	case 3:
		return 4 //Игровое
	case 4:
		return 5 //Учитель
	case 2:
		return 6 //Плохое
	case 5:
		return 7 //Агрессивное
	default:
		return 0
	}
	return 0
}

// 1 - 17  нажаты кнопки оператором
func getPrioritetOfPultButtonActions(actionID int) int {
	switch actionID {
	case 6:
		return 1 //Успокоить
	case 16:
		return 2 //Простить
	case 9:
		return 3 //Игнорировать
	case 11:
		return 4 //Сделать приятно
	case 2:
		return 5 //Наказать
	case 1:
		return 6 //Непонятно
	case 17:
		return 7 //Вылечить
	case 5:
		return 8 //Накормить
	case 14:
		return 9 //Обрадоваться
	case 15:
		return 10 //Испугаться
	case 12:
		return 11 //Заплакать
	case 7:
		return 12 //Предложить поиграть
	case 8:
		return 13 //Предложить поучить
	case 13:
		return 3 //Засмеяться
	case 4:
		return 15 //Поощрить
	case 10:
		return 16 //Сделать больно
	case 3:
		return 17 //Наказать

	default:
		return 0
	}
	return 0
}

/////////////////////////////////////////

/*
	определить ID ситуации: настроение при посылке сообщения, нажатые кнопки и т.п.

Может быть выбрана только одна из существующих ситуаций, поэтому выбор идет по приоритетам.
для определения узла SituationID дерева.
Это определяет контекст ситуации, при вызове активации дерева понимания.
В getCurSituationImageID() по-началу выбирается наугад (для первого приближения) более важные из существующих,
но потом дерево понимания может переактивироваться с произвольным заданием контекста.
От этого параметра зависит в каком направлении пойдет информационный поиск решений,
если не будет запущен штатный автоматизм ветки (ориентировочные реакции).
Инфа curActiveActions обновляется при каждой активации дерева моторных автоматизмов.

Функция возвращает предположительный ID смыслового контекста ситуации:

	1 - было ответное действие (смотреть в автоматизме ветки Usefulness int - (БЕС)ПОЛЕЗНОСТЬ: вред: -10 0 +10 +n польза diffPsyBaseMood )
	2 - был запуск автоматизма ветки.
	3 - ничего не делали, но нужно осмысление
	4 - все спокойно, можно экспериментивароть
	5 - оператор не прореагировал на действия в течение периода ожидания - игнорирует? нужно достучаться?

	10-17 - оператор выбрал настроение (14 - Учитель при отправке)
	20-37 - оператор нажал кнопку (17 - Игровое при отправке или нажал кнопку Поиграть)
	... и т.п.
*/
func getCurSituationImageID() int {
	if detectedActiveLastNodID == 0 {
		// ничего не делали, но нужно осмысление
		id, _ := createSituationImage(0, detectedActiveLastNodID, 3, true)
		if id > 0 {
			return id
		}
		return 0
	}
	var sitID = 0

	if LastRunAutomatizmPulsCount > 0 { // был и закончился ответом период ожидания на действия автоматизма
		// вышло время ожидания реакции
		if (LastRunAutomatizmPulsCount + WaitingPeriodForActionsVal) < PulsCount {
			// оператор не прореагировал на действия в течение периода ожидания - игнорирует? нужно достучаться?
			id, _ := createSituationImage(0, detectedActiveLastNodID, 5, true)
			if id > 0 {
				return id
			}
		}
		//было ответное действие (смотреть в автоматизме ветки Usefulness int - (БЕС)ПОЛЕЗНОСТЬ: вред: -10 0 +10 +n польза diffPsyBaseMood )
		id, _ := createSituationImage(0, detectedActiveLastNodID, 1, true)
		if id > 0 {
			return id
		}
	} //if LastRunAutomatizmPulsCount > 0

	// ЕСТЬ ЛИ АВТОМАТИЗМ В моторной ВЕТКЕ и болеее ранних?
	if currentAutomatizmAfterTreeActivatedID > 0 {
		//был запуск автоматизма ветки
		id, _ := createSituationImage(0, detectedActiveLastNodID, 2, true)
		if id > 0 {
			return id
		}
	}

	/////////////// ситуации действий с пульта
	var max = 0
	// сначала настроение, чтобы оно перекрылось кнопками действий если они есть
	if curActiveActions != nil {
		mood := curActiveActions.MoodID
		if mood != 0 { // есть настроение
			prior := getPrioritetOfPultMoodActions(mood)
			if max < prior {
				sitID = 10 + mood
				max = prior
			}
		}

		// кнопки действий
		aArr := curActiveActions.ActID
		if aArr != nil { // есть действия кнопок с Пульта
			max = 0 // перекрываем
			for i := 0; i < len(aArr); i++ {
				prior := getPrioritetOfPultButtonActions(aArr[i])
				if max < prior {
					sitID = 20 + aArr[i]
					max = prior
				}
			}
		}
	} /////////////////////

	if sitID > 0 {
		id, _ := createSituationImage(0, detectedActiveLastNodID, sitID, true)
		if id > 0 {
			return id
		}
	} //////////////////////////////

	// все спокойно, можно экспериментивароть
	id, _ := createSituationImage(0, detectedActiveLastNodID, 4, true)
	if id > 0 {
		return id
	}

	return 0
}

/////////////////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_stimul.go
____________________________________________
/* Понимание стимула


 */

package psychic

/////////////////////////////////////////////////////
/* распознаватель: не навязывают ли стимулом то, что не соотвествует текущей Теме и Цели?
У всех природных тварей важно строить поведение в направлении собственных потребностей, а не чужих, не попадать под воздействие чужих интересов, не выполнять чужие приказы, не учить то, что противоречит собственной адаптивности.
Уже в период преступной инициативы возникает доминирование некоего Эго – как противодействие чужому влиянию.
Например, если тварь наказывается за что-то, что отвечает ее интересам, то она должна этому противодействовать.

Меняет состояние CurrentInformationEnvironment.isStimulToForce:
	true - текущий стмул навязывает то, что не соотвествует текущей Теме и Цели

При CurrentInformationEnvironment.isStimulToForce=true игнорируется штатный автоматизм, останавливая все более низкоуровневое.
Начинается цикл подбора подходящей для Темы и Цели реакциию.

Эта функция может быть сделана множеством способов, порождая особенности реагирования стадии Инициативы.
*/
func recognizerStimulToForce() {
	CurrentInformationEnvironment.isStimulToForce = false
	//Это начинает работать с 4-й стадии (преступной инициативы).
	if EvolushnStage < 4 {
		return
	}
	if isIdleness() { // ЛЕНЬ
		return
	}
	// если ВАЖНАЯ ИЛИ ОПАСНАЯ СИТУАЦИЯ
	if CurrentInformationEnvironment.veryActualSituation || CurrentInformationEnvironment.danger {
		return
	}

	// антагонистично ли собственное состояние или нет
	var isAntagonizm = false
	/* цель определяется в infoFunc8() и заполняется структура problemTreeInfo.

		problemTreeInfo.themeID:
		1 "Негативный эффект моторного автоматизма"
		2 "Негативный эффект ментального автоматизма"
		3 "Состояние Плохо"
		4 "Стимул с Пульта"
		5 "Поисковый интерес"
		6 "Обучение с учителем"
		7 "Игнорирование оператором"
		8 "Игра"
		9 "Неудовлетворенность существущим"
		10 "Непонимание"
		11 "Разговор"
		12 "Сомнение в штатном автоматизме"
		13 "Защита"
		14 "Страх"
		15 "Агрессия"
		16 "Есть объект высокой значимости"
		17 "Улучшение настроения"

		problemTreeInfo.purposeID:
		Создается (в infoFunc8() createPurposeImageID()) образ цели типа PurposeImage
		type PurposeImage struct {
			ID int
			// цель - добиться target
			target int// 1- повторения, 2 - улучшения
			//добиться target значения PsyBaseMood -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
			moodeID int // этот параметр будет всегда считаться заданным в getMentalPurposeEffect().
	// В случае Цели - улучшить эмоцию (target==2) - имеется в виду, что сумма весов позитивных эмоциональных контекстов превышает сумму вемов негатиных:	func isEmotonBetter(oldID int, curID int)
		emotonID int// добиться target такой эмоции

		// В случае Цели - улучшить ситуацию - func isSituationBetter(oldID int, curID int)
		situationID int// добиться PurposeImage.target данной ситуации SituationImage
	}
	*/
	var isActualTheme = false // есть актуальная для антагонизма Тема
	// текущая Тема:
	if problemTreeInfo.themeID > 0 {
		tID := problemTreeInfo.themeID
		// если нужен вес темы, то:
		//ThemeImageMapCheck()
		//themeType := ThemeImageFromID[problemTreeInfo.themeID].Type
		if tID == 1 || tID == 2 || tID == 9 || tID == 12 || tID == 13 || tID == 15 {
			isActualTheme = true
		}
	}
	// текущая цель всегда есть:
	if problemTreeInfo.purposeID > 0 {
		//		problem:=PurposeImageFromID[problemTreeInfo.purposeID]
		problem, ok := ReadePurposeImageFromID(problemTreeInfo.purposeID)
		if !ok {
			return
		}
		if isActualTheme && (problem.target == 2 || problem.emotonID > 0 || problem.situationID > 0) {
			isAntagonizm = true
		}

	}
	if !isAntagonizm { // нет причин для сопротивления
		return
	}

	if curActiveActions == nil {
		return
	}
	/* настроение при передаче фразы с Пульта:
	   	20-Хорошее    21-Плохое    22-Игровое    23-Учитель    24-Агрессивное   25-Защитное    26-Протест
	   ID возникает при добавлении 19 к номеру радиокнопки пульта, например, для Хорошее 1+19=20
	*/
	if curActiveActions.MoodID > 19 {
		mood := curActiveActions.MoodID
		if mood == 21 || mood == 24 || mood == 25 || mood == 26 {
			CurrentInformationEnvironment.isStimulToForce = true
			return
		}
		// м.б. еще какое-то распознавание ...
	}

	// сориентироваться по действия с пульта
	if curActiveActions.ActID != nil {
		act := curActiveActions.ActID
		for i := 0; i < len(act); i++ {
			if act[i] == 3 || act[i] == 9 || act[i] == 10 {
				CurrentInformationEnvironment.isStimulToForce = true
				return
			}
		}
		// м.б. еще какое-то распознавание ...
	}

	/* сориентироваться по фразе с пульта

	 */
	if curActiveActions.PhraseID != nil {

		// м.б. еще какое-то распознавание ...
	}

}

////////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/understanding_theme_image.go
____________________________________________
/* Тема мышления - для выбора Цели
АКТУАЛЬНАЯ ТЕМА (mentalInfoStruct.ThemeImageType) изменятеся по событиям ThemeTypeStr
с перезапуском размышления runNewTheme(Weight,Type)
после чего в infoFunc8() выбирается цель mentalInfoStruct.mentalPurposeID

Тема определяется по многим разным объективным событиям (с помощью runNewTheme)
и в такой момент она запускает новый цикл размышления,
если только вес предыдущей темы не больше. В начале цикла по теме определяется цель (мтивация, потребность).
И циклы направляются в сторону достижения этой цели.
События изменения темы могут быть как при Стимуде (с Пульта), так и вне его,
так что сами по себе запускают новый цикл размышления.

Тема удаляется из ThemeImageFromID при решении проблемы или остается нерешенной и 
тогда становится Доминантой.

ID темы используется в ментальных Правилах (как и ID Цели): вместо ID дерева автоматизмов+ситуации как в моторных Правилах, используются ID темы+Цели
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/* какие бывают темы для ThemeImage.Type (многие из type SituationImage struct {)
фактически - перечисление проблем, 
которые могут стать доминантой если ThemeImage.PulsCount старый и ThemeImage.Weight >3

Поиск по проекту детектора данной темы: "runNewTheme(N,"

1 "Негативный эффект моторного автоматизма"
2 "Негативный эффект ментального автоматизма"
3 "Состояние Плохо"
4 "Стимул с Пульта"
5 "Поисковый интерес"
6 "Обучение с учителем"
7 "Игнорирование оператором"
8 "Игра"
9 "Неудовлетворенность существущим"
10 "Непонимание"
11 "Разговор"
12 "Сомнение в штатном автоматизме"
13 "Защита"
14 "Страх"
15 "Агрессия"
16 "Есть объект высокой значимости"
17 "Улучшение настроения"
*/
var ThemeTypeStr =[]string{
"Нет темы", //0
"Негативный эффект моторного автоматизма", //1  -ЕСТЬ ДЕТЕКТОР runNewTheme(1,
"Негативный эффект ментального автоматизма", //2 -ЕСТЬ ДЕТЕКТОР runNewTheme(2,
"Состояние Плохо", //3 Очень плохо -ЕСТЬ ДЕТЕКТОР runNewTheme(3,
"Стимул с Пульта", //4 -ЕСТЬ ДЕТЕКТОР runNewTheme(4,
"Поисковый интерес", //5 - любопытство, экспериментирование, набор опыта  -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Обучение с учителем", //6 -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Игнорирование оператором", //7 оператор не ответил в течение периода ожидания на важный запрос  -ЕСТЬ ДЕТЕКТОР
"Игра", //8  -ЕСТЬ ДЕТЕКТОР runNewTheme(8, -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Неудовлетворенность существущим", //9  -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Непонимание", //10  -ЕСТЬ один вид непонимания: runNewTheme(10,1)
"Действие оператора", //11 текстовое общение с оператором -ЕСТЬ ДЕТЕКТОР runNewTheme(11,
"Сомнение в штатном автоматизме", //12  проблема mentalInfoStruct.motorAtmzmID > 0 -ЕСТЬ ДЕТЕКТОР runNewTheme(12,
"Защита", //13 -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Страх", //14 -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Агрессия", //15 -ЕСТЬ ДЕТЕКТОР в simeThemeDetection()
"Есть объект высокой значимости", // 16 -ЕСТЬ ДЕТЕКТОР runNewTheme(16,
"Улучшение настроения", //17 - базовая тема когда нет ничего другого, после просыпания и т.п.
}
// название типа темы
func GetThemeImageName(id int)string{
return ThemeTypeStr[id]
}
//////////////////////////////////////////////


/* образ темы
*/
type ThemeImage struct {
	ID int
	// вес значимости для конкурентности 1 - 10 Используется только в runNewTheme( для конкурентности.
	Weight int // типичное значение ==2 если не нужно чтобы эта тема доминировала
	Type int //ID типов тем одно значение из ThemeTypeID
	PulsCount int// время актуализации темы
}
//var ThemeImageFromID=make(map[int]*ThemeImage)
var ThemeImageFromID []*ThemeImage  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteThemeImageFromID(index int, value *ThemeImage) {
	addThemeImageFromID(index)
	ThemeImageFromID[index] = value
}
func addThemeImageFromID(index int){
	if index >= len(ThemeImageFromID) {
		newSlice := make([]*ThemeImage, index+1)
		copy(newSlice, ThemeImageFromID)
		ThemeImageFromID = newSlice
	}
}
// считывание члена
func ReadeThemeImageFromID(index int) (*ThemeImage,bool){
	if index >= len(ThemeImageFromID) || ThemeImageFromID[index]==nil {
		return nil,false
	}
	return ThemeImageFromID[index],true
}
///////////////////////////////////////////////////////////////////


/* создать новый образ темы, если такой еще нет
createThemeImageID(id int,2,3,LifeTime,true) 
*/
var lastThemeImageThemeID=0
func createThemeImageID(id int,Weight int,Type int,PulsCount int,CheckUnicum bool)(int,*ThemeImage){
	if CheckUnicum {
		oldID,oldVal:=checkUnicumThemeImage(Type)
		if oldVal!=nil{
			// обновить время создания и вес
			oldVal.PulsCount=LifeTime
			oldVal.Weight=Weight
			return oldID,oldVal
		}
	}

	if id==0{
		lastThemeImageThemeID++
		id=lastThemeImageThemeID
	}else{
		if lastThemeImageThemeID<id{
			lastThemeImageThemeID=id
		}
	}

	var node ThemeImage
	node.ID = id
	node.Weight=Weight
	node.Type = Type
	node.PulsCount=PulsCount

	//ThemeImageFromID[id]=&node
	WriteThemeImageFromID(id, &node)

	if doWritingFile {SaveThemeImageFromIdArr() }

	return id,&node
}
func checkUnicumThemeImage(Type int)(int,*ThemeImage){

	for id, v := range ThemeImageFromID {
		if v==nil{continue}
		if Type!=v.Type {
			continue
		}
		return id,v
	}
	return 0,nil
}

// сохранить образы
func SaveThemeImageFromIdArr(){
	var out=""
	for k, v := range ThemeImageFromID {
		if v==nil{continue}
		out+=strconv.Itoa(k)+"|"
		out+=strconv.Itoa(v.Weight)+"|"
		out+=strconv.Itoa(v.Type)+"|"
		out+=strconv.Itoa(v.PulsCount)
		out+="\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/Theme_images.txt",out)
}

//  загрузить образы, обнуляя вес старых тем
func loadThemeImageFromIdArr(){

	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/Theme_images.txt")
	cunt:=len(strArr)
	//ThemeImageFromID=make(map[int]*ThemeImage)
	ThemeImageFromID = make([]*ThemeImage, cunt)//задать сразу имеющиеся в файле число
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		p:=strings.Split(strArr[n], "|")
		id,_:=strconv.Atoi(p[0])
		weight,_:=strconv.Atoi(p[1])
		kind,_:=strconv.Atoi(p[2])
		pulsCount,_:=strconv.Atoi(p[3])
		// если возраст темы > 10дней - убрать ее конкурентный вес
		if LifeTime - pulsCount > 864000{// //нельзя удалять тему т.к. она используется в ментральных Правилах
			//continue
			weight=0
		}

		var saveDoWritingFile= doWritingFile; doWritingFile =false
		createThemeImageID(id,weight,kind,pulsCount,false)
		doWritingFile =saveDoWritingFile
	}
	return
}

/*  Детектор появления новой темы - по событиям из ThemeTypeStr
Если появляется новая тема, но ее вес меньше старой, то думается про старую.

Тема меняется только с помощью этой функции!

Старое размышление не прерывается, а создается новый цикл.
 */

var oldThemeID=0 // код карты ThemeImageFromID, это не ThemeImage.Type
func runNewTheme(kind int,weight int){
	// только со стадии развития > 3
	if EvolushnStage < 4 {
		return
	}
themeID,_:=createThemeImageID(0,weight,kind,LifeTime,true)
/* новая тема может ли перекрыть старую?
т.е. исчерпана ли старая тема, имеет ли она меньший вес, чем новая?
 */
	node,ok:=ReadeThemeImageFromID(oldThemeID)
	if !ok {
		return
	}
if oldThemeID>0 && node.Weight > weight{
	return // остается старая тема, размышление не прерывается на новую
}

if oldThemeID != themeID {// если тема сменилась
	mentalInfoStruct.ThemeImageType = kind
	oldThemeID = themeID

	//инфа для активации дерева проблем:
	problemTreeInfo.themeID=themeID

	// !!!!  interruptMentalWork()
	cycle := createNewCycleIteration()
		themrCycle=cycle
		cycle.log+="Определена тема для мышления: "+ThemeTypeStr[kind]+"<br>"
		cycle.log+="Запущен новый цикл <span style='color:#006300' onclick='get_cycle_info("+strconv.Itoa(cycle.ID)+")'><b>"+strconv.Itoa(cycle.ID)+"</b></span><br>"
		cycle.themeType=kind
		//infoFunc8(cycle) // определить mentalInfoStruct.ThemeImageType и актиуивровать дерево проблем
		return
}
return
}
// жестко начать новую тему, даже если такая уже была перед этим
func NewTeme(){
	mentalInfoStruct.ThemeImageType =0
	oldThemeID=0
}
/////////////////////////////////////////////////////////

/*изменение веса темы после размышления или обнуление веса неактуальной темы:
 */
func ThemeAfterThinking(themeID int,weight int){
	if weight < 0{// удалить неактуальную тему
//нельзя удалять тему т.к. она используется в ментральных Правилах
		//delete(ThemeImageFromID, themeID)
		//return
		weight=0
	}
	if weight > 10{
		weight=10
	}
//	ThemeImageFromID[themeID].Weight=weight
	node,ok:=ReadeThemeImageFromID(themeID)
	if ok {
		node.Weight=weight
	}
}

/* детектор некоторых тем в зависимости от условий
Распознает тему и запускает ее итерацию 
Вызов только из одного места: перед концом объективного вызова func consciousnessElementary( в understanding.go
 */
var themrCycle *cycleInfo
func someThemeDetection()bool{
	 themrCycle=nil //или только перекрывать?
//	if mentalInfoStruct.motorAtmzmID>0{ уже прошли 1 и 2 уровни
//		return false
//	}
	var themType=0// тип темы
	var themWeight=0// вес темы
	// плохо и опасно
	if PsyMood < -8 && CurrentInformationEnvironment.veryActualSituation && CurrentInformationEnvironment.danger {
		runNewTheme(10, 3)
		return true
	} else { // нет атаса
		if PsyMood>0 && !CurrentInformationEnvironment.danger && !existsBaseContext(3) {
			if existsBaseContext(2){// текущая эмоция - поиск
				runNewTheme(5,5)
				return true
			}
			if existsBaseContext(3){// текущая эмоция - игра
				runNewTheme(8,2)
				return true
			}
			if existsBaseContext(2) && existsBaseContext(3){// текущая эмоция - поиск+игра
				runNewTheme(8,2)//- обучение
				return true
			}

		}
		if existsBaseContext(5) {//- защита
			runNewTheme(13,5)
			return true
		}
		if existsBaseContext(8) {//- страх
			runNewTheme(14,5)
			return true
		}
		if existsBaseContext(9) {//- агрессия
			runNewTheme(15,3)
			return true
		}

		// Неудовлетворенность существующим
		if existsBaseContext(6) && existsBaseContext(11) {//- лень+доброта
			runNewTheme(9,2)// ОТ ВЕСА МНОГОЕ ЗАВИСИТ
			return true
		}
	}

// если есть какая-то проблема:
	if themType>0 && themWeight>0{
		runNewTheme(themType,themWeight)
		return true
	}
	return false
}
///////////////////////////////////////////////////////





____________________________________________

BOT/brain/psychic/understanding_tree.go
____________________________________________
/* Дерево понимания ситуации

формат записи: ID|ParentNode|Mood|EmotionID|SituationID

При активации дерева ситуации текущая ситуация
UnderstandingNodeFromID[detectedActiveLastUnderstandingNodID].SituationID
и записывается в
curBaseStateImage.SituationID
*/

package psychic

import (
	"BOT/brain/gomeostas"
)

var noProblemTreeActivation = false // true - не активировать дерево проблем

// инициализирующий блок - в порядке последовательности инициализаций
// из psychic.go
func UnderstandingTreeInit() {
	if EvolushnStage < 4 { // только со стадии развития 4
		return
	}
	loadPurposeImageFromIdArr()
	loadUnderstandingTree()
	if len(UnderstandingTree.Children) == 0 { // еще нет никаких веток
		// создать первые три ветки базовых состояний
		createBasicUnderstandingTree()
	}
}

/* ДЕРЕВО понимания ситуации.
Имеет фиксированных 3 уровня (кроме базового нулевого)
формат записи: ID|ParentNode|Mood|EmotionID|SituationID
Узлы всех уровней могут произвольно меняться на другие для переактивации Дерева.

Дерево может переактивароваться при срабатывании мент.автоматизмов с действиями
MentalActionsImages.activateBaseID и MentalActionsImages.activateEmotion
в mental_automatizm_actions.go RunMentalAutomatizm(
*/
type UnderstandingNode struct { // узел дерева ситуации
	ID int
	//Mood = PsyBaseMood: -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
	Mood      int // ощущаемое настроение МОЖЕТ ПРОИЗВОЛЬНО МЕНЯТЬСЯ
	EmotionID int // эмоция, МОЖЕТ ПРОИЗВОЛЬНО МЕНЯТЬСЯ
	/* SituationID определяет основной контекст ситуации, определяемый при вызове активации дерева понимания.
	   Если этот контекст не задан в understandingSituation(situationImageID
	   то в getCurSituationImageID() по-началу выбирается наугад (для первого приближения) более важные из существующих,
	   но потом дерево понимания может переактивироваться с произвольным заданием контекста.
	   От этого параметра зависит в каком направлении пойдет информационный поиск решений,
	   если не будет запущен штатный автоматизм ветки (ориентировочные реакции).
	*/
	SituationID int // ID объекта структуры понимания SituationImage, может произвольно меняться

	Children   []UnderstandingNode // дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID   int                 // ID родителя
	ParentNode *UnderstandingNode  // адрес родителя
}

var UnderstandingTree UnderstandingNode

//var UnderstandingNodeFromID=make(map[int]*UnderstandingNode)
var UnderstandingNodeFromID []*UnderstandingNode // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteUnderstandingNodeFromID(index int, value *UnderstandingNode) {
	addUnderstandingNodeFromID(index)
	UnderstandingNodeFromID[index] = value
}
func addUnderstandingNodeFromID(index int) {
	if index >= len(UnderstandingNodeFromID) {
		newSlice := make([]*UnderstandingNode, index+1)
		copy(newSlice, UnderstandingNodeFromID)
		UnderstandingNodeFromID = newSlice
	}
}

// считывание члена
func ReadeUnderstandingNodeFromID(index int) (*UnderstandingNode, bool) {
	if index >= len(UnderstandingNodeFromID) || UnderstandingNodeFromID[index] == nil {
		return nil, false
	}
	return UnderstandingNodeFromID[index], true
}

//////////////////////////////////////////////////

// последовательность узлов активной ветки
//var ActiveBranchUnderstandingArr []int

// если в результате ментальных процессов было действие, то нужно заблокировать обработку активации дерева моторных автоматизмов
var MentalReasonBlocing = false

/* попытка активации дерева ментальных автоматизмов

 */
var detectedActiveLastUnderstandingNodID = 0

// нераспознанный остаток - НОВИЗНА
var CurrentUnderstandingTreeEnd []int
var currentUnderstandingStepCount = 0

// массив узлов активной ветки   currentUnderstandingActivedNodes=getcurrentUnderstandingActivedNodes(lastID)
var currentUnderstandingActivedNodes []*UnderstandingNode // вначале конечный узел
// последовательность узлов активной ветки подряд
var activeUnderstandingNodeArr []int

//текущие образы  гомеостатической этиологии, колторые могут быть произвольно перекрыты ментальными образами
var newMoodID = 0
var newEmotionID = 0
var newSituationID = 0

//сохраненные образы гомеостатической этиологии, колторые могут быть произвольно перекрыты ментальными образами
var preMoodID = 0
var preEmotionID = 0
var preSituationID = 0

/* Активация дерева понимания ситуации происходит из:
func afterTreeActivation() - при каждой активации automatism_tree.go
и если было действия без ответа в течении 20 пульсов, то understandingSituation вызывается из
func noAutovatizmResult()
т.е. оба деревав работают совместно при EvolushnStage > 3 и по каждой активации UnderstandingTree
добавляется эпизд памяти newEpisodeMemory()

Аналогично дереву моторных автоматзмов, после активации могут быть ориентировочные рефлексы привлечения внимания.

При вызове может быть определен situationImageID или проставлен 0 и тогда образ ситуации определяется в самой функции.

Если были совершены действия, то нужно выставлять MotorTerminalBlocking=true !!!

activationType =1 - объективная активация
activationType =2 - произвольная переактивация

return true// заблокировать все низкоуровневое
*/
func understandingSituation(activationType int) {
	MentalReasonBlocing = false // true - заблокировать все рефлексы и штатные автоматизмы

	if EvolushnStage < 4 { // только со стадии развития 4
		return
	}
	if PulsCount < 4 { // не активировать пока все не устаканится
		return
	}
	// определить ID ситуации: настроение при посылке сообщения, нажатые кнопки и т.п.
	situationImageID := getCurSituationImageID()
	if situationImageID == 0 { // нет выбранной ситуации
		return
	}
	/*
		BaseStateImageMapCheck()
		if BaseStateImageArr != nil{ // перекрыть curBaseStateImage
			curBaseStateImage = *BaseStateImageArr[situationImageID]
			curBaseStateImage.SituationID = situationImageID
		}*/

	//	ps:=getPurposeGenetic() // - тут уже сохраняется savePurposeGenetic
	//	savePurposeGenetic=ps

	detectedActiveLastUnderstandingNodID = 0 // ID конечного узла активной ветки

	activeUnderstandingNodeArr = nil
	CurrentUnderstandingTreeEnd = nil
	currentUnderstandingStepCount = 0
	currentUnderstandingActivedNodes = nil

	// текушие гомео-зависимые параметы
	newMoodID = PsyBaseMood

	bsIDarr := gomeostas.GetCurContextActiveIDarr()
	newEmotionID, _ = createNewBaseStyle(0, bsIDarr, true)

	/* не просрочены ли произвольно активированные параметры
	Держатся на время, пока не изменятся генетически определенные соотвествующие параметры или
	если активация была в данном пульсе
	*/
	if mentalMoodVolitionPulsCount != PulsCount && preMoodID != newMoodID {
		mentalMoodVolitionID = 0
	}
	if mentalEmotionVolitionPulsCount != PulsCount && preEmotionID != newEmotionID {
		mentalEmotionVolitionID = 0
	}
	if mentalSituationVolitionPulsCount != PulsCount && preEmotionID != newSituationID {
		mentalSituationVolitionID = 0
	}

	// сохранять только изменившиеся значения
	if preMoodID != newMoodID {
		preMoodID = newMoodID
	}
	if preEmotionID != newEmotionID {
		preEmotionID = newEmotionID
	}
	if preSituationID != newSituationID {
		preSituationID = newSituationID
	}

	// 3 уровня условий в виде ID их образов

	var lev1 = newMoodID // PsyBaseMood: -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
	if mentalMoodVolitionID > 0 {
		lev1 = mentalMoodVolitionID
	}

	var lev2 = newEmotionID
	if mentalEmotionVolitionID > 0 {
		lev2 = mentalEmotionVolitionID
	}

	var lev3 = situationImageID
	if mentalSituationVolitionID > 0 {
		lev3 = mentalSituationVolitionID
	}

	condArr := getUnderstandingActiveConditionsArr(lev1, lev2, lev3)
	// основа дерева
	cnt := len(UnderstandingTree.Children)
	for n := 0; n < cnt; n++ {
		node := UnderstandingTree.Children[n]
		lev1 := node.Mood
		if condArr[0] == lev1 {
			detectedActiveLastUnderstandingNodID = node.ID
			ost := condArr[1:]
			if len(ost) == 0 {

			}

			conditionUnderstandingFound(1, ost, &node)

			break // другие ветки не смотреть
		}
	}

	// результат поиска:
	if detectedActiveLastUnderstandingNodID > 0 {
		// есть ли неучтенные условия?
		conditionsCount := getConditionsCount(condArr)
		CurrentUnderstandingTreeEnd = condArr[currentUnderstandingStepCount:] // НОВИЗНА
		if currentUnderstandingStepCount < conditionsCount {                  // не пройдено до конца имеющихся условий
			// нарастить недостающее в ветке дерева
			detectedActiveLastUnderstandingNodID = formingUnderstandingBranch(detectedActiveLastUnderstandingNodID, currentUnderstandingStepCount, condArr)
		}
	} else { // вообще нет совпадений для данных условий
		// нарастить недостающее в ветке дерева
		detectedActiveLastUnderstandingNodID = formingUnderstandingBranch(detectedActiveLastUnderstandingNodID, currentUnderstandingStepCount, condArr)
		CurrentUnderstandingTreeEnd = condArr // все - новизна
	}

	//инфа для активации дерева проблем:
	problemTreeInfo.autTreeID = detectedActiveLastNodID
	problemTreeInfo.situationTreeID = situationImageID

	// все узлы активной ветки, в начале - конечный узел
	currentUnderstandingActivedNodes = getcurrentUnderstandingActivedNodes(detectedActiveLastUnderstandingNodID)

	// здесь - новые темы при активации деревьев, т.к. для дерева проблем нужен ID дерева ситуации
	if activationType == 1 && needRunNewTheme > 0 {
		if ActivationTypeSensor > 1 { // есть инфа о curActions - параметры действий оператора
			NewTeme() // тут всегда обнулять тему т.к. - новое воздействие оператора
		}
		runNewTheme(needRunNewTheme, 2) //после активации дерева ситуации
	}

	/* объективный запуск consciousnessElementary - по акативации дерева автоматизмов
	ментальный запуск в случае произвольной переактивации дерева понимания или цикле осмысления (5-я ступень)

	При вызове  infoFunc8() происходит активация дерева проблем и НЕ ДОЛЖНА БЫТЬ новая об.активация осмысления!
	*/
	if !noProblemTreeActivation {
		activationType = 1
		// перезапустить дерево проблем
		MentalReasonBlocing = consciousnessElementary() // тут перезапускается главный цикл мышления
	} else { // была переактивация дерева из func infoFunc8
		if wasRunPurposeActionFunc { // перективация запущена из func infoFunc14
			wasRunPurposeActionFunc = false
			// начать новый главный цикл мышления
			resetMineCycleAndBeginAsNew()
		}
	}

	return
}

func conditionUnderstandingFound(level int, cond []int, node *UnderstandingNode) {
	if cond == nil || len(cond) == 0 {
		return
	}

	ost := cond[1:]

	for n := 0; n < len(node.Children); n++ {
		cld := node.Children[n]
		var levID = 0
		switch level {
		case 0:
			levID = cld.Mood
		case 1:
			levID = cld.EmotionID
		case 2:
			levID = cld.SituationID
		}
		if cond[0] == levID {
			detectedActiveLastUnderstandingNodID = cld.ID
			activeUnderstandingNodeArr = append(activeUnderstandingNodeArr, cld.ID)
		} else {
			currentUnderstandingStepCount = level - 1
			continue
		}

		level++
		currentUnderstandingStepCount = level
		conditionUnderstandingFound(level, ost, &node.Children[n])
		return // раз совпало, то другие ветки не смотреть
	}
	return
}

____________________________________________

BOT/brain/psychic/understanding_tree_functions.go
____________________________________________
/* Функции для Дерева понимания (дерева ментальных автоматизмов)
запись ID|ParentNode|Mood|EmotionID|SituationID
*/

package psychic

import (
	"BOT/lib"
	"strconv"
	"strings"
)

/* Создать новый узел дерева понимания.
Формат записи:
ID|ParentNode|Mood|EmotionID|SituationID
*/
var lastUnderstandingNodeID=0
func createNewUnderstandingNode(parent *UnderstandingNode,id int,Mood int,EmotionID int,
	SituationID int,CheckUnicum bool)(int,*UnderstandingNode){

	if parent == nil{
		return 0,nil
	}

	// если есть такой узел, то не создавать
	if CheckUnicum {
		idOld,nodeOld:=FindUnderstandingTreeNodeFromCondition(Mood,EmotionID,SituationID)
		if idOld>0{
			return idOld,nodeOld
		}
	}

	if id==0{
		lastUnderstandingNodeID++
		id=lastUnderstandingNodeID
	}else{
		if lastUnderstandingNodeID<id{
			lastUnderstandingNodeID=id
		}
	}

	var node UnderstandingNode
	node.ID = id
	node.ParentNode=parent
	node.ParentID = parent.ID

	node.Mood=Mood
	node.EmotionID=EmotionID
	node.SituationID=SituationID

	parent.Children = append(parent.Children, node)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *UnderstandingNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
		}
	}

//	UnderstandingNodeFromID[id]=&node
	WriteUnderstandingNodeFromID(id, &node)

	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в UnderstandingNodeFromID:
	updateUnderstandingNodeFromID(parent)// здесь потому, что при загрузке из файла нужно на лету получать адреса

	return id,newN
}

// создать первые три ветки базовых состояний
func createBasicUnderstandingTree(){
	notAllowScanInTreeThisTime=true // запрет показа карты при обновлении

	//PsyBaseMood: -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
	createNewUnderstandingNode(&UnderstandingTree,0,-1,0,0,false)
	createNewUnderstandingNode(&UnderstandingTree,0,0,0,0,false)
	createNewUnderstandingNode(&UnderstandingTree,0,1,0,0,false)
	if doWritingFile {SaveUnderstandingTree() }
	// SaveUnderstandingTree()
	notAllowScanInTreeThisTime = false // запрет показа карты при обновлении
}

// корректируем адреса всех узлов
func updateUnderstandingNodeFromID(parent *UnderstandingNode){
	//updatingUnderstandingNodeFromID(&VernikePhraseTree)
	updatingUnderstandingNodeFromID(parent)
}

// проход всего дерева
func updatingUnderstandingNodeFromID(rt *UnderstandingNode){
	if rt.ID>0 {
//		rt.ParentNode=UnderstandingNodeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		node,ok:=ReadeUnderstandingNodeFromID(rt.ParentID)
		if ok {
			rt.ParentNode=node
			UnderstandingNodeFromID[rt.ID] = rt
		}
	}
	if rt.Children == nil{// конец ветки
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		updatingUnderstandingNodeFromID(&rt.Children[i])
	}
}

/* загрузить записанное дерево
Формат записи:
ID|ParentNode|Mood|EmotionID|SituationID
*/
func loadUnderstandingTree(){
	//нулевой узел
	//UnderstandingNodeFromID[0]=&UnderstandingTree// все по нулям по умолчанию
	
	//UnderstandingNodeFromID[0]=rt
	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/understanding_tree.txt")
	cunt:=len(strArr)
	UnderstandingNodeFromID = make([]*UnderstandingNode, cunt)//задать сразу имеющиеся в файле число
	WriteUnderstandingNodeFromID(0, &UnderstandingTree)// все по нулям по умолчанию
	//просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n])==0{
			continue
		}
		if len(strArr[n])<2{
			panic("Сбой загрузки дерева ситуации: ["+strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p:=strings.Split(strArr[n], "|")
		id,_:=strconv.Atoi(p[0])
		parentID,_:=strconv.Atoi(p[1])
		Mood,_:=strconv.Atoi(p[2]) // PsyBaseMood: -1 Плохое настроение, 0 Нормальное, 1 - хорошее настроение
		EmotionID,_:=strconv.Atoi(p[3])
		SituationID,_:=strconv.Atoi(p[4])

		// новый узел с каждой строкой из файла
		var saveDoWritingFile= doWritingFile; doWritingFile =false
		node,ok:=ReadeUnderstandingNodeFromID(parentID)
		if ok {
			createNewUnderstandingNode(node, id, Mood, EmotionID, SituationID, false)
		}
		doWritingFile =saveDoWritingFile
	}
	return
}

// ID|ParentNode|Mood|EmotionID|SituationID
func SaveUnderstandingTree(){
	if EvolushnStage < 4 { // только со стадии развития 4
		return
	}
	notAllowScanInTreeThisTime=true
	var out=""
	cnt:=len(UnderstandingTree.Children)
	for n := 0; n < cnt; n++ {// чтобы записывалось по порядку родителей
		out+=getUnderstandingNode(&UnderstandingTree.Children[n])
	}

	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/understanding_tree.txt",out)
	notAllowScanInTreeThisTime = false
	return
}

// такой проход чтодбы дочерние узлы шли по порядку и всегда были бы родители
func getUnderstandingNode(wt *UnderstandingNode)(string){
	var out="" //ID|ParentNode|Mood|EmotionID|SituationID
	//	if wt.ParentID>0 {
	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|"
	out += strconv.Itoa(wt.Mood) + "|"
	out += strconv.Itoa(wt.EmotionID) + "|"
	out += strconv.Itoa(wt.SituationID)
	out +="\r\n"
	//	}
	if(wt.Children==nil){// конец
		return out
	}
	for n := 0; n < len(wt.Children); n++ {
		out+=getUnderstandingNode(&wt.Children[n])
	}
	return out
}

// найти КОНЕЧНЫЙ узел по условиям
func FindUnderstandingTreeNodeFromCondition(Mood int,EmotionID int,
	SituationID int)(int,*UnderstandingNode){
	/*
	for k, v := range UnderstandingNodeFromID {
	if v==nil{continue}
		if v.Mood==Mood && v.EmotionID==EmotionID &&
			v.SituationID==SituationID {
			return k, v
		}
	}
	*/
	var id=0
	var aut *UnderstandingNode
	cnt:=len(UnderstandingTree.Children)
	for n := 0; n < cnt; n++ {
		id,aut=checkUnderstandingTree(&UnderstandingTree.Children[n],Mood,EmotionID,SituationID)
		if id >0{
			return id,aut
		}
	}
	return 0,nil
}

func checkUnderstandingTree(v *UnderstandingNode,Mood int,EmotionID int,
	SituationID int)(int,*UnderstandingNode){
	var id=v.ID
	var aut =v

	// как только наткнется в предыдущих на такое услове - выдаст ID этой ветки
	if v.Mood==Mood && v.EmotionID==EmotionID && v.SituationID==SituationID  {
		return v.ID,v
	}

	if v.Children==nil {// конец
		return 0,nil
	}
	for n := 0; n < len(v.Children); n++ {
		id,aut=checkUnderstandingTree(&v.Children[n],Mood,EmotionID,SituationID)
		if id>0{
			return id,aut
		}
	}
	return 0,nil //v.ID

}

// выдать массив узлов ветки по конечному ID, начиная с конечного к первому
func getcurrentUnderstandingActivedNodes(lastID int)([]*UnderstandingNode){
	var nodws []*UnderstandingNode
//node:=UnderstandingNodeFromID[lastID]
	node,ok:=ReadeUnderstandingNodeFromID(lastID)
	if !ok {
		return nil
	}
for node!=nil{
	nodws=append(nodws,node)
	node=node.ParentNode
}
return nodws
}

/* создание иерархии АКТИВНЫХ образов контекстов условий и пусковых стимулов в виде ID образов в [4]int
создать последовательность уровней условий в виде массива  ID последовательности ID уровней
*/
func getUnderstandingActiveConditionsArr(lev1 int, lev2 int, lev3 int)([]int){
	arr:=make([]int, 4)
	arr[0]=lev1
	arr[1]=lev2
	arr[2]=lev3
	return arr
}

// создание новой ветки, начиная с заданного узла
func addNewUnderstandingBranchFromNodes(level int,cond []int,node *UnderstandingNode)(int){
	if node==nil {
		return 0
	}
	if level>=len(cond) {
		return node.ID
	}
	var id=0
	switch(level){
	case 0:
		id,_=createNewUnderstandingNode(node,0,cond[0],0,0,true)
	case 1:
		id,_=createNewUnderstandingNode(node,0,cond[0],cond[1],0,true)
	case 2:
		id,_=createNewUnderstandingNode(node,0,cond[0],cond[1],cond[2],true)
	case 3:
		id,_=createNewUnderstandingNode(node,0,cond[0],cond[1],cond[2],true)
	}
	level++
	unode,ok:=ReadeUnderstandingNodeFromID(id)
	if ok {
		id = addNewUnderstandingBranchFromNodes(level, cond, unode)
		return id
	}
	return 0
}

// создание ветки, начиная с заданного узла fromID
func formingUnderstandingBranch(fromID int,lastLevel int,condArr []int)(int){
	// нарастить ветку недостающим
//	lastNode:=UnderstandingNodeFromID[fromID]
	lastNode,ok:=ReadeUnderstandingNodeFromID(fromID)
	if ok {
		lastNodeID := addNewUnderstandingBranchFromNodes(lastLevel, condArr, lastNode)
		if lastNodeID > 0 {
			// SaveUnderstandingTree() // сохранять в общем порядке, при закрытии и по времени сохранения
		}
		return lastNodeID
	}
	return 0
}

// выдать массив узлов ветки по заданному ID узла
func getBrangeUnderstandingNodeArr(lastNodeId int)([]*UnderstandingNode){
	var nArr []*UnderstandingNode
//	node:=UnderstandingNodeFromID[lastNodeId]
	node,ok:=ReadeUnderstandingNodeFromID(lastNodeId)
	if !ok {
		return nil
	}
	for {
		if node==nil {
			break
		}
		nArr = append(nArr, node)
		node=node.ParentNode
	}
	return nArr
}

// выдать массив ID узлов ветки по заданному ID узла
func getBrangeUnderstandingNodeIdArr(lastNodeId int)([]int){
	var nArr []int
//	node:=UnderstandingNodeFromID[lastNodeId]
	node,ok:=ReadeUnderstandingNodeFromID(lastNodeId)
	if !ok {
		return nil
	}
	for {
		if node==nil {
			break
		}
		nArr = append(nArr, node.ID)
		node=node.ParentNode
	}
	return nArr
}
____________________________________________

BOT/brain/psychic/understanding_tree_orientation.go
____________________________________________
/* Ориентировочный рефлекс осознания ситуации.

В любом случае orientationConsciousness(kind int) - осмысление ситуации SituationImage

Попытка подставить вместо текущего автоматизма более правильный в новых условиях - ор.рефлекс в дереве
Оценка результата автоматизма и корректировка мозжечковыми рефлексами
ПРИ ОСОЗНАНИИ ЦЕЛИ И ПЕРЕБОРА_НЕДОБРА, А ТАК ЖЕ НЕОБХОДИМОСТИ ДОПОЛНИТЕЛЬНЫХ (корректирующих)ДЕЙСТВИЙ.
*/

package psychic

///////////////////////////////////////



/* kind:
0 - вообще еще нет ветки с нуля - полная новизна условий
1 - частино есть ветка - частичная новизна условий
2 - ветка полностью есть, только новизна ситуации

Доступная Инфа:
curActiveActions - структура действий при активации дерева автоматизмов
savePurposeGenetic
saveSituationImageID
currentMentalAutomatizmID
LastRunAutomatizmPulsCount - идет период ожидания ответа
LastAutomatizmWeiting - ожидается результат запущенного MotAutomatizm

// нераспознанный остаток - НОВИЗНА: var CurrentUnderstandingTreeEnd []int

return true - были совершены моторные действия - заблокировать все более низкоуровневые действия
*/


// НЕ НУЖЕН ор.рефлекс !!!!



____________________________________________

BOT/brain/psychic/verbal_Broca_img.go
____________________________________________
/* Словестные образы (область Брока)  для 4, 5 и 6-го уровня дерева автоматизмов.
Смысл (осознанную значимость) образ приобретеает только в контексте Дерева Понимания (дерева мент.автоматизмов)

Детекторы зоны Вернике распознают слова и словосочетания,
а область Брока отвечает за смысл распознанных слов и словосочетений,
за конструирование собственных словосочетаний,
за моторное использование слов и словосочетаний.
За все ответственная структура - образ осмысленных слов и сочетаний.

! Нужно иметь в виду, что в Vernike_detector.go есть массив памяти фраз, накапливается в течении дня
var MemoryDetectedArr []MemoryDetected - структур фразы с контекстным окружением
и Verbal.PhraseID[] - можно найти в этом массиве для ориентировки что бы ло раньше и позже.
MemoryDetectedArr - как бы оперативная память фраз для сопоставлений.
*/

package psychic

import (
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
	"strings"
)

/////////////////////////////////////////////

func verbalInit() {
	loadVerbalFromIdArr()

	/*
	   var tm=922// "Обычный, Хорошее"
	   	str:=getToneMoodStrFromID(tm)
	   	if len(str)>0{	}
	*/

}

////////////////////////////////////////////

/*
	для оптимизации поиска по дереву перед узлом Verbal идет узел первого символа : var symbolsArr из word_tree.go

Смысл (осознанную значимость) образ приобретеает только в контексте Дерева Понимания (дерева мент.автоматизмов)
Если член PhraseID[] == -1 - фраза есть, но нераспознана.

Используется в purpose_genetic.go и rules_functions.go
Создается при активации дерева автоматизмов, НО ТАМ ВЕТКА PhraseID int//!!!! на самом деле пишется lev6 = verb.PhraseID[0] !!!
*/
type Verbal struct {
	ID int
	// для текущего сообщения с Пусльта:
	SimbolID int   // id первого символа первой фразы PhraseID: var symbolsArr из word_tree.go
	PhraseID []int // массив фразID (DetectedUnicumPhraseID) слова каждой фразы вытаскиваются wordSensor.GetWordArrFromPhraseID(PhraseID[0])
	//0 - обычный, 1 - восклицательный, 2- вопросительный, 3- вялый, 4 - Повышенный
	ToneID int // тон сообщения с Пульта
	//0 - обычный, 1 (из 20)-Хорошее    2 (21)-Плохое    3(22)-Игровое    4(23)-Учитель
	//5(24)-Агрессивное   6(25)-Защитное    7(26)-Протест
	MoodID int // настроение оператора
}

// var VerbalFromIdArr=make(map[int]*Verbal)
var VerbalFromIdArr []*Verbal // сам массив
// var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteVerbalFromIdArr(index int, value *Verbal) {
	addVerbalFromIdArr(index)
	VerbalFromIdArr[index] = value
}
func addVerbalFromIdArr(index int) {
	if index >= len(VerbalFromIdArr) {
		newSlice := make([]*Verbal, index+1)
		copy(newSlice, VerbalFromIdArr)
		VerbalFromIdArr = newSlice
	}
}

// считывание члена
func ReadeVerbalFromIdArr(index int) (*Verbal, bool) {
	if index >= len(VerbalFromIdArr) || VerbalFromIdArr[index] == nil {
		return nil, false
	}
	return VerbalFromIdArr[index], true
}

//////////////////////////////////////////
// для поиска по ID фразы
//var VerbalFromPhraseIdArr=make(map[int]*Verbal)
///////////////////////////////////////////

// создать образ сочетаний пусковых стимулов
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0| - ID=2
var lastVerbalID = 0

func createNewlastVerbalID(id int, SimbolID int, PhraseID []int, ToneID int, MoodID int, CheckUnicum bool) (int, *Verbal) {
	if MoodID > 19 {
		MoodID = MoodID - 19
	} else {
		MoodID = 0
	}
	if CheckUnicum {
		oldID, oldVal := checkUnicumVerbal(PhraseID, ToneID, MoodID)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastVerbalID++
		id = lastVerbalID
	} else {
		//		newW.ID=id
		if lastVerbalID < id {
			lastVerbalID = id
		}
	}

	var node Verbal
	node.ID = id
	node.SimbolID = SimbolID
	node.PhraseID = PhraseID
	node.ToneID = ToneID
	node.MoodID = MoodID

	//VerbalFromIdArr[id]=&node
	WriteVerbalFromIdArr(id, &node)

	return id, &node
}
func checkUnicumVerbal(PhraseID []int, ToneID int, MoodID int) (int, *Verbal) {
	for id, v := range VerbalFromIdArr {
		if v == nil {
			continue
		}
		if !lib.EqualArrs(PhraseID, v.PhraseID) {
			continue
		}
		if ToneID != v.ToneID || MoodID != v.MoodID {
			continue
		}
		return id, v
	}

	return 0, nil
}

// ///////////////////////////////////////
// создать новый вербальный образ, если такого еще нет
func CreateVerbalImage(FirstSimbolID int, PhraseID []int, ToneID int, MoodID int) (int, *Verbal) {
	if PhraseID == nil || isUnrecognizedPhraseFromAtmtzmTreeActivation {
		return 0, nil
	}
	// достаем первый символ первой фразы
	// получить последователньость wordID из уникального идентификатора первой фразы
	/*
	   	wordIDarr:=wordSensor.GetWordArrFromPhraseID(PhraseID[0])
	   	// первое слово в виде строки
	   	if wordIDarr==nil || len(wordIDarr)==0{
	   		return 0,nil
	   	}
	   	word:=wordSensor.GetWordFromWordID(wordIDarr[0])
	   	//rw:=[]rune(word)
	   	//SimbolID:=wordSensor.GetSymbolIDfromRune(rw[0])
	   //	word:=wordSensor.GetPhraseStringsFromPhraseID(PhraseID[0])
	   	//SimbolID:=wordSensor.GetSymbolIDfromString(rw[0])
	*/
	id, verb := createNewlastVerbalID(0, FirstSimbolID, PhraseID, ToneID, MoodID, true)

	if doWritingFile {
		SaveVerbalFromIdArr()
	}

	return id, verb
}

/////////////////////////////////////////

// ////////////////// сохранить образы сочетаний пусковых стимулов
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0|
func SaveVerbalFromIdArr() {
	var out = ""
	for k, v := range VerbalFromIdArr {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		out += strconv.Itoa(v.SimbolID) + "|"
		for i := 0; i < len(v.PhraseID); i++ {
			out += strconv.Itoa(v.PhraseID[i]) + ","
		}
		out += "|"
		out += strconv.Itoa(v.ToneID) + "|"
		out += strconv.Itoa(v.MoodID) + "|"
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/verbal_images.txt", out)

}

// //////////////////  загрузить образы сочетаний пусковых стимулов
func loadVerbalFromIdArr() {

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_psy/verbal_images.txt")
	cunt := len(strArr)
	//VerbalFromIdArr=make(map[int]*Verbal)
	VerbalFromIdArr = make([]*Verbal, cunt) //задать сразу имеющиеся в файле число
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		SimbolID, _ := strconv.Atoi(p[1])
		s := strings.Split(p[2], ",")
		var PhraseID []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			si, _ := strconv.Atoi(s[i])
			PhraseID = append(PhraseID, si)
		}
		ToneID, _ := strconv.Atoi(p[3])
		MoodID, _ := strconv.Atoi(p[4])
		var saveDoWritingFile = doWritingFile
		doWritingFile = false
		createNewlastVerbalID(id, SimbolID, PhraseID, ToneID, MoodID, false)
		doWritingFile = saveDoWritingFile
	}
	return

}

//////////////////////////////

/*
	получить уникальное сочетание в виде int из двух компонентов int

На входе int2 -  виде ID настроения (jn 20 до 26) преобразуется в диапазон от 1 до 7
простым вычитанием int2-19
*/
func GetToneMoodID(int1 int, int2 int) int {
	// вмето первой 0 (для "обычный") ставим 9 !!!
	if int1 == 0 {
		int1 = 9
	}
	s := strconv.Itoa(int1)
	if int2 >= 19 {
		int2 -= 19
	}
	s += strconv.Itoa((int2))
	ToneMoodID, _ := strconv.Atoi(s)
	return ToneMoodID
}

// ////////////////////////////////
// получить тон и настроение из уникального сочетания
func GetToneMoodFromImg(img int) (int, int) {
	tonmoode := strconv.Itoa(img)
	var t = 0
	ton := tonmoode[:1]
	if ton == "9" {
		t = 0
	} else {
		t, _ = strconv.Atoi(ton)
	}
	m, _ := strconv.Atoi(tonmoode[1:])
	return t, m
}

// расшифровка в виде строки
func GetToneMoodStrFromID(img int) string {
	t, m := GetToneMoodFromImg(img)
	out := "<b>" + getToneStrFromID(t) + " - "
	out += getMoodStrFromID(m) + "</b>"

	return out
}

//////////////////////////////////////////////////

// /////////////////////////////
func getToneStrFromID(id int) string {
	var ret = ""
	//0 - обычный, 1 - восклицательный, 2- вопросительный, 3- вялый, 4 - Повышенный
	switch id {
	case 0:
		ret = "обычный"
	case 1:
		ret = "восклицательный"
	case 2:
		ret = "вопросительный"
	case 3:
		ret = "вялый"
	case 4:
		ret = "Повышенный"
	default:
		ret = "ощибка (тон = " + strconv.Itoa(id) + ")"
	}
	return ret
}

// //////////////////////////////
func getMoodStrFromID(id int) string {
	var ret = ""
	// из 20-Хорошее    21-Плохое    22-Игровое    23-Учитель    24-Агрессивное   25-Защитное    26-Протест
	// отнимаем 19
	switch id {
	case 0:
		ret = "Нормальное"
	case 1:
		ret = "Хорошее"
	case 2:
		ret = "Плохое"
	case 3:
		ret = "Игровое"
	case 4:
		ret = "Учитель"
	case 5:
		ret = "Агрессивное"
	case 6:
		ret = "Защитное"
	case 7:
		ret = "Протест"

	}
	return ret
}

////////////////////////////////

// выдать инфу про PhraseID
func GetStringsFromVerbalID(vID int) string {

	//	v:=VerbalFromIdArr[vID]
	v, ok := ReadeVerbalFromIdArr(vID)
	if !ok {
		lib.WritePultConsol("Нет карты VerbalFromIdArr для iD=" + strconv.Itoa(vID))
		return "Ошибка получения информации."
	}
	out := "Вербальный образ ID=" + strconv.Itoa(vID)

	out += "<br> Фраза: "
	for i := 0; i < len(v.PhraseID); i++ {
		if i > 0 {
			out += " | "
		}
		out += wordSensor.GetPhraseStringsFromPhraseID(v.PhraseID[i]) + "<br>"
	}
	out += "Тон: " + getToneStrFromID(v.ToneID) + "<br>"
	out += "Настроение: " + getMoodStrFromID(v.MoodID) + "<br>"
	return out
}

// ///////////////////////////////////////////////////
// выдать инфу про PhraseID
func GetPraseStringsFromVerbalID(vID int) string {

	//	v:=VerbalFromIdArr[vID]
	v, ok := ReadeVerbalFromIdArr(vID)
	if !ok {
		lib.WritePultConsol("Нет карты VerbalFromIdArr для iD=" + strconv.Itoa(vID))
		return "Ошибка получения информации."
	}
	out := ""
	for i := 0; i < len(v.PhraseID); i++ {
		if i > 0 {
			out += " | "
		}
		out += wordSensor.GetPhraseStringsFromPhraseID(v.PhraseID[i]) + "<br>"
	}

	return out[:len(out)-4]
}

/////////////////////////////////////////////////////

// ПРОИЗВОЛЬНОЕ ТВОРЕНИЕ ФРАЗ

/*
	сделать фразу PhraseID из wordID []

Возвращает PhraseID
*/
func AddwordIDToPhraseTree(wordID []int) []int {
	// засунуть фразу в дерево слов и дерево фраз
	// проход одной фразы - распознавание ID слов фразы
	wordSensor.PhraseDetection(wordID, false)
	PhraseID := wordSensor.CurrentPhrasesIDarr

	// первый символ ответной фразы
	FirstSimbolID := wordSensor.GetFirstSymbolFromPraseID(PhraseID)
	// создать образ Брока
	CreateVerbalImage(FirstSimbolID, PhraseID, 0, 0)

	return PhraseID
}

////////////////////////////////////////////

/*
	сделать фразу PhraseID из string

Возвращает PhraseID
*/
func AddStringToPhraseTree(str string) []int {
	// засунуть фразу в дерево слов и дерево фраз
	wordSensor.VerbalDetection(str, 0, 0, 0)
	PhraseID := wordSensor.CurrentPhrasesIDarr

	// первый символ ответной фразы
	FirstSimbolID := wordSensor.GetFirstSymbolFromPraseID(PhraseID)
	// создать образ Брока
	CreateVerbalImage(FirstSimbolID, PhraseID, 0, 0)

	return PhraseID
}

////////////////////////////////////////////

// есть ли в данном Verb фраза praseID int
func existsPraseIDinVerbID(verbID int, praseID int) bool {
	verb, ok := ReadeVerbalFromIdArr(verbID)
	if !ok {
		return false
	}
	for n := 0; n < len(verb.PhraseID); n++ {
		if verb.PhraseID[n] == praseID {
			return true
		}
	}
	return false
}

////////////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/volition.go
____________________________________________
/* ПРОИЗВОЛЬНОСТЬ - все, что требует изменения текущего автоматизма.

функции запуска предположительного варианта, противоречащего автоматизму.
*/

package psychic

/////////////////////////////////////////////

____________________________________________

BOT/brain/psychic/volition_functions.go
____________________________________________
/* 
функции произвольной регуляции: от активации Базового состояния и эмоций с переакотивацией деревьев, 
поиска в эпиз.памяти и дереве понимания и т.п. 

*/


package psychic


/////////////////////////////////////////

____________________________________________

BOT/brain/psychic/x_comparison_and_generalization.go
____________________________________________
/*
	Сопоставление и обобщение в системе Beast

Общеразделяемые определения.
Операция обобщения — логическая операция, в процессе которой производится переход от понятия с меньшим объемом к понятию с большим объемом; при этом содержание понятия становится беднее. В основе операции обобщения — отношение подчинения.
Обобщение как операция мышления включает в себя отбрасывание единичных признаков при сохранении общих, с раскрытием существенных связей.
Обобще́ние поня́тий — логическая операция, посредством которой в результате исключения видового признака получается другое понятие более широкого объёма, но менее конкретного содержания; форма превращения знания путём мысленного перехода от частного к общему в некоторой модели мира, что обычно соответствует и переходу на более высокую ступень абстракции.

Сопоставление и обобщение в системе Beast применяется на 5-й стадии развития, при работе с доминантами нерешенной проблемы.

Пример: "если человек вошёл в комнату и увидел детей, держащих в руках фломастеры, а затем увидел каракули на всех стенах, логика подсказала бы, что на основании представленной информации дети рисовали фломастерами на стенах."
ЭТО НЕ ЛОГИКА
"дети с фломастерами и разрисованные стены" позволяют сразу сделать вывод о том, что стены разрисовали дели фломастерами, а не инопланетяне с целью ввести в заблуждение. У размышления была цель: понять причину разрисованности стен. Модель стен имеет кадры опыта, когда взрослый видел разрисовывающих детей, при сопоставлении с моделью стен (фактически тот же опыт) сразу возникает самый общий результат, и цель размышления достигается. При этом нет никаких логических рассуждений по формальным правилам какой-то из логик. А есть просто результат обобщения моделей.

Обобщение всегда сводится к выявлению "длинных" Правил.
Простое Правило - это Стимул->Ответ->Эффект, а длинное может состоять из нескольких стимулов подряд, нескольких ответов и одного общего эффекта.
Образ "цифра два" вместе с образом "операция сложения" и с образом "цифра три" дает составной Стимул обобщения.
Образ составного действия, которое определяет алгоритм в отношении обработки образов стимула дает Ответ обобщения
Эффект же зависит от того, насколько верным оказывается Ответ. В разных условиях Эффект одного обобщения может оказаться разным.

Дерево эпиз.памяти, хранящее простые Правила является самым примитивным видом обобщения. Но из нескольких таких правил (сопоставляемых между собой) может быть сформировано одно обобщение, которое включает в себя некоторые или все сопоставляемые правила.

Кроме того, при сопоставлении образов длинного текста, содержащего слова с известным смыслом (Модель понимания образов), могут выявляться обобщающие их сочетания и строится предполагаемое обобщающее правило, которое замет может быть проверено в подходящих условиях (наличие сочетания стимулов обобщения) реальным запуском Ответа данного обобщения.
При неполном распознавании фразы тоже нужно обобщение (var curActiveActionsPhraseNonRecognised=false// не была распознана фраза).
При синтезе новой фразы func infoFunc12 - используются длинные правила.

В mental_memories.go уже заготовлены глобальные массивы памяти для обобщений, которые, видимо, НЕНУЖНЫ:
var saveBaseLinksCicleSynthesis []*int
var SynthesizeImportanceMemory []*SynthesizeImportanceImage

В принципе вся информация для обобщений есть в эпиз.памяти. НО
В структуре автоматизма возможно ветвление цепочки действий:
Automatizm.ActionsImageID -> Automatizm.NextID
В структуре самого действия тоже есть ветвление:
ActionsImage.ActID []int если член ActID имеет ID>prefixActionIdValue
*/
package psychic

import (
	word_sensor "BOT/brain/words_sensor"
)

//////////////////////////////////////////////////////// TODO

/*
для неопределенной фразы
найти слово в стимуле правил для текущих условий и более общих, но не во всей эп.памяти

	оценивая значимость стимула с таким словом и игнорируя малую значимость.
	Вернуть акцию наиболее успешного правила

# Для func infoFunc300
Новые фразы:
привет тебе!
ты очень классный
ну-ка играй
Возвращает массиы ID действий с позитивным Эфектом
*/
func getBestActionFromWordID(wordArr []int) int {
	/* !!! если нераспознана фраза, то detectedActiveLastNodID НЕВЕРЕН его нельзя использовать как поиск по эп.памяти
	поэтому нужно использовать lastWellPhrasedetectedActiveLastNodID
	Но func getWellRulesFromWordId и не испоьзует detectedActiveLastNodID
	а поиск только по узлу настроения и эмоции.
	*/

	var rullArr []Rule
	for i := 0; i < len(wordArr); i++ {
		if wordArr[i] == -1 {
			continue
		}
		// сначала смотрим правила в которых есть такое слово
		exactly := true //точно для условий выдать []Rule для данного ID фразы с позитивным Эффектом
		rArr := getWellRulesFromWordId(wordArr[i], exactly)
		if rArr != nil {
			for j := 0; j < len(rArr); j++ {
				rullArr = append(rullArr, rArr[j])
			}
			_, r := findBestRule(rullArr)
			if r.Action > 0 {
				return r.Action
			}
		}

		exactly = false // выдать []Rule для данного ID фразы с позитивным Эфектом
		rArr = getWellRulesFromWordId(wordArr[i], exactly)
		if rArr != nil {
			for j := 0; j < len(rArr); j++ {
				rullArr = append(rullArr, rArr[j])
			}
			_, r := findBestRule(rullArr)
			if r.Action > 0 {
				return r.Action
			}
		}
	}

	return 0
}

///////////////////////////////////////////////////////////

/*
	найти в неопределенной фразе что-то ранее встречающееся и выдать известные образы

# Для func infoFunc300
Новые фразы:
привет тебе!
ты очень классный
ну-ка играй
Возвращает массиы ID действий с позитивным Эфектом
*/
func prepareUnrecognizedPhrase(fraseArr []int) int {

	// по каждой фразе анализируем и при уверенном результате даже не смотрим дальше, а используем этот результат
	for i := 0; i < len(fraseArr); i++ {
		if fraseArr[i] == -1 { // фраза не распознанна
			continue
		}
		// сначала смотрим правила в которых есть такая фраза
		exactly := true //точно для условий выдать []Rule для данного ID фразы с позитивным Эффектом
		rArr := getWellRoolesFromPhraseId(fraseArr[i], exactly)
		if rArr != nil {
			_, r := findBestRule(rArr)
			if r.Action > 0 {
				return r.Action
			}
		}

		exactly = false // выдать []Rule для данного ID фразы с позитивным Эфектом
		rArr = getWellRoolesFromPhraseId(fraseArr[i], exactly)
		if rArr != nil {
			_, r := findBestRule(rArr)
			if r.Action > 0 {
				return r.Action
			}
		}

		wArr := word_sensor.GetWordsArrFromPraseID(fraseArr[i])
		if wArr != nil {
			wordsModel := getWordArrModelExactly(wArr)
			if wordsModel.beastFrameID > 0 { // ID наилучшего по эффекту кадра памяти
				actionNode, ok := ReadeEpisodicTreeNodeFromID(wordsModel.beastFrameID)
				if ok {
					return actionNode.Action
				}
			}
		}
	}

	return 0
}

///////////////////////////////////////////////////////////

/*
	Анализировать длинную или недоопределенную фразу, найти обобщающие данные и что-то сделать

# Для func infoFunc30

Привет!
Хочу научить тебя простым вещам.
Для начала давай усвоим длинные тексты.
В них могут попадаться знакомые значащие слова вроде улыбнуться, давай учить, бестолочь.
Ты должен найти их, выделить правила и обобщить слова в ответное общее действие.

GetPhraseStringsFromPhraseID выдает результат:
привет!#хочу научить тебя простым вещам.#для начала давай усвоим длинные тексты.#в них могут попадаться знакомые значащие слава вроде улыбнуться, давай учить, бестолочь.#ты должен найти их, выделить правила и обобщить слава в ответное общее действие.

Возвращает
*/
func preparelongPhrase(fraseArr []int) bool {
	//	var lineArr [][]int
	//	s := ""

	// по каждой фразе анализируем и при уверенном результате даже не смотрим дальше, а используем этот результат
	for i := 0; i < len(fraseArr); i++ {
		if fraseArr[i] == -1 { // фраза не распознанна
			continue
		}
		// сначала смотрим правила в которых есть такая фраза
		exactly := true //точно для условий выдать []Rule для данного ID фразы с позитивным Эфектом
		rArr := getWellRoolesFromPhraseId(fraseArr[i], exactly)
		if rArr != nil {

			// TODO что-то сделать с этим массивовм правил
			return true
		}

		exactly = false // выдать []Rule для данного ID фразы с позитивным Эфектом
		rArr = getWellRoolesFromPhraseId(fraseArr[i], exactly)
		if rArr != nil {

			// TODO что-то сделать с этим массивовм правил
			return true
		}

		wArr := word_sensor.GetWordsArrFromPraseID(fraseArr[i])
		if wArr != nil {
			wordsModel := getWordArrModelExactly(wArr)
			if wordsModel.beastFrameID > 0 || wordsModel.worstFrameID > 0 { // есть значения

				// TODO что-то сделать с этим результатом
				return true
			}
		}

		// ничего не обнаружено

		/*		wArr := word_sensor.GetWordsArrFromPraseID(fraseArr[i])
				if wArr == nil {
					continue
				}
				lineArr = append(lineArr, wArr)
				// проверка
				for n := len(wArr) - 1; n >= 0; n-- {
					s += word_sensor.GetWordFromWordID(wArr[n]) + " "
				}
				s += "\n" */
	} //for i := 0; i < len(fraseArr); i++ {

	/*	if lineArr == nil { // нет строк сообщения
			return
		}
		for i := 0; i < len(lineArr); i++ {

		}*/
	return false
}

///////////////////////////////////////////////////////////

____________________________________________

BOT/brain/puls.go
____________________________________________
package brain

import (
	"BOT/brain/reflexes"
	word_sensor "BOT/brain/words_sensor"
	"BOT/lib"
	_ "fmt"
	"strconv"
	"time"
)

/* Пульс - сердце Beast :)
запускает  каждую секунду Аctivatin() в net.go, поддерживая возможность самостоятельных действий Beast
В общем, это обеспечивает возможность параллельной работы функций Beast, отводя им выполнение с каждым пульсом
*/

var PulsCount = 0 		// счетчик пульса
var noWorkung = false // не активировать очередной цикл если true - тишина
var startWait = 0  		// начало ожидания
var secWaiting = 1 		// время ожидания
var LifeTime int 			// время жизни в числе пульсов
var EvolushnStage int // стадия развития

func init() {
// Puls() Запускается только в одном месте (main.go) чтобы не было двух потоков пульса!!!
}

// сохранить время жизни в файл
func saveLifeTime() {
	if LifeTime>10 {// иногда life_time.txt обнулялся...
		lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/life_time.txt", strconv.Itoa(LifeTime))
	}
}

/* запуск пульса по сигналам из getParams := r.FormValue("get_params")

 */
var blockinfP=0
func SincroTic(){
	blockinfP=1
	Puls()
}
////////////////////////////////


//ВОДИТЕЛЬ РИТМА ПУЛЬСА
var IsPultActivnost = false	// начало активности с Пульта  brain.IsPultActivnost=true brain.IsPultActivnost=false
func Puls() {
	if blockinfP>0{// не запускать генеретор пульса в этот раз - синхронизация из Пульта
		pulsActions()// действия для этого такта пульса
		blockinfP=0
		return
	}
	pulsActions()


	sleepPuls()
	/*
	time.AfterFunc(1 * time.Second, func() {
		// действия дял этого такта пульса
		pulsActions()
		Puls()
	})*/
}
func sleepPuls() {
	pChannel := make(chan int,1)
	go func() {
		pChannel <- 1
		timer := time.NewTimer(1 * time.Second)
		select {
		case <-timer.C:
			Puls()
		}
		close(pChannel) // закрываем канал
	}()
}
///////////////////////////////////////////




/* действия, совершаемые по каждому пульсу:
буквально вся работа восприяти - действия идет последовательно,
так что pulsActions() ждет пока все не выполниться.
 */
var isBusyFromWork=false // если pulsActions() не успело выполниться, то пропускает вызов текущего пульса
func pulsActions(){

	if isBusyFromWork{
		return
	}
	isBusyFromWork=true // если pulsActions() не успело выполниться, то пропускает текущий цикл
	// сканирование состояния (пульс):
	//now := time.Now()
	//curTime := now.Second()//.Millisecond()
	//log.Println("пульс",curTime)
	if PulsCount == 2 {
		lib.WritePultConsol("Beast активируется.")
	}
	/* ЗАЧЕМ ОСТАНАВЛИВАТЬ АКТИВНОСТЬ ПРИ СОБЫТИИ С ПУЛЬТА??
		 NotAllowAnyActions=true должно использоваться ТОЛЬКО для остановки активности дял ЗАПИСИ ПАМЯТИ!
	if  IsPultActivnost{// остановка любой активности Beast
		NotAllowAnyActions=true
	}else{
		NotAllowAnyActions=false
	}
	*/
	if !noWorkung {
		Activation(LifeTime) // Главный цикл (пульс) опроса состояния Beast и его ответов в brain.go
	}
	if startWait > 0 && !IsPultActivnost { // ждем следующего цикла и выполняем inaction
		if PulsCount > (startWait + 1) {
			inaction()
			noWorkung = false // конец ожидания
		}
	}
	LifeTime++
	//if (PulsCount + 1) % 10 == 0 {
	//	saveLifeTime()
	//}
	//ac := time.Date(0, time.January, 1, 0, 0, 0, 0, time.UTC)
	//lastPulsTime=curTime
	PulsCount++

	isBusyFromWork=false // если pulsActions() не успело выполниться, то пропускает текущий цикл

	// не было активности с пульта и нет перегруза (раз дошло до сюда)
	if !reflexes.IsPultActionThisPuls && word_sensor.NeedCheckTempList {
		word_sensor.NeedCheckTempList=false// только 1 раз
		word_sensor.UpdateWordTreeFromTempArr(3, 5)
	}
}
///////////////////////////////////////////////////

var NotAllowAnyActions = false // - запрет любой активности  brain.NotAllowAnyActions
// то, что должно выполнять в тишине, в бездействии
func inaction() {
	/* Память сохраняется при корректном выключении, попытки автоматизировать - очень геморные и ненадежные
	// сохранять все каждые 100 сек если нет действий
	if (PulsCount+1)%100==0{
		NotAllowAnyActions=true
		SaveAll()
		NotAllowAnyActions=false
	}
	*/
}

/* если нужно остановить все на время waitSec для выполнения какой-то функции, то
Пример для функции созранения всей памяти:
func WaitSaveAllPryMemory(){
	OwnFuncForRun=SaveAllPryMemory
	StopAll() // выполнить  saveAllPryMemory() в тишине, когда ничто не работает
}
*/
func StopAll() {
	noWorkung = true	// останавливает puls()
	startWait = PulsCount
}

func StopRunAll(stop bool) {
	if stop {// останавливает puls()
		noWorkung = true
	} else {
		noWorkung = false
	}
}
____________________________________________

BOT/brain/reflexes/0_reflexes_main.go
____________________________________________
/* основыне данные по рефлексам

 */

package reflexes

import (
	"BOT/brain/gomeostas"
	"BOT/brain/psychic"
	"BOT/brain/transfer"
)

//////////////////////////////////////////////////////
// отслеживане времени с последнего изменения условий с Пульта в пульсах
var lastActivnostFromPult = 0

// было изменение активности с пульта в текущем пульсе. Только одна активность допускается в течение пульса.
var activetedPulsCount = 0 // против многократных срабатываний

// переключатель игрового режима  reflexes.IsGameMode
//var IsGameMode=false

// true - была активность с Пульта в perception.go
var IsPultActionThisPuls=false

// ПУЛЬС рефлексов
var ReflexPulsCount = 0 // передача тика Пульса из brine.go
var LifeTime = 0 				// время жизни
var EvolushnStage = 0 	// стадия развития
var IsSlipping = false  // флаг фазы сна

// коррекция текущего состояния гомеостаза и базового контекста с каждым пульсом
func ReflexCountPuls(evolushnStage int, lifeTime int, puls int, isSlipping bool) {
	IsPultActionThisPuls=false
	LifeTime = lifeTime
	EvolushnStage = evolushnStage
	ReflexPulsCount = puls // передача номера тика из более низкоуровневого пакета
	IsSlipping = isSlipping

	if puls == 4{
		psychic.PsychicInit() // после 3-го пульса!
	}
	if puls == 5{
		testingRunMakeAutomatizmsFromReflexes()
	}

	if activetedPulsCount != ReflexPulsCount { // защита от повторных срабатываний
		if gomeostas.IsNewConditions { // изменились условия
			ActiveFromConditionChange()
			lastActivnostFromPult=ReflexPulsCount
			return
		}
		/* если условия не меняются более 20 сек, то пусть срабатывает простейший инстинкт
		только если Базоваое состояние Плохо или Хорошо
		*/
		if ReflexPulsCount - lastActivnostFromPult > 20{
			bc := gomeostas.CommonBadNormalWell
			if bc != 2 {
				// найти и выполнить простейший безусловный рефлекс
				findAndExecuteSimpeReflex()
			}
			if EvolushnStage==1{
				transfer.IsPsychicGameMode = false //автоматический сброс игрового режима для первой стадии, автоматическая активация делается в activeGameMode()
			}
			lastActivnostFromPult = ReflexPulsCount // новый период ослеживания
		}
	}


	// обнулить причину возможного запуска рефлекса
	if oldActiveCurTriggerStimulsID > 0 && oldActiveCurTriggerStimulsPulsCount > (ReflexPulsCount + 10) || transfer.IsPsychicGameMode == false {
		oldActiveCurTriggerStimulsID = 0
	}

}
/////////////////////////////////////////////////
____________________________________________

BOT/brain/reflexes/automatizms_from_reflexes.go
____________________________________________
/* Создать автоматизмы на основе существующих рефлексов

Запускается по ссылке не ранее 4-го пульса, так что должны быть готовы все массивы.

Для тестирования возможно избежать долгий период воспитания с формированием автоматизмов и просто сгенерировать автоматизмы на основе существующих рефлексов (с приоритетом условных рефлексов).
При этом у автоматизмов будут установлены опции уже проверенного автоматизма с полезностью, равной 1 (вполне полезно). Это правомерно потому, что рефлексы создавались уже полезными для своих условий.
В дальнейшем такие автоматизмы будут проверяться в зависимости от реакции оператора и изменения состояния Beast, корректируясь настолько эффективно, насколько позволяет текущая стадия развития.
*/

package reflexes

import (
	"BOT/brain/psychic"
	wordSensor "BOT/brain/words_sensor"
	"sort"
	"strconv"
)

func testingRunMakeAutomatizmsFromReflexes() {
	// RunMakeAutomatizmsFromReflexes()
	// RunMakeAutomatizmsFromGeneticReflexes()
}

/*
	сканировать для всех условных рефлексов,

создавать ветку дерева автоматизма если такой еще нет,
создавать автоматизм, прикрепляя его к нужно ветке.
*/
func RunMakeAutomatizmsFromReflexes() string {
	// проверить готовность рабочих массивов и сообщить если нет
	if ConditionReflexes == nil || len(ConditionReflexes) == 0 ||
		psychic.AutomatizmTreeFromID == nil || len(psychic.AutomatizmTreeFromID) == 0 {
		return "Еще не сформировалась оперативная память, пожалуйста перезапустите процесс через пару секунд."
	}
	var newCount = 0
	var count = 0
	// сортировка по ID чтобы тестировалось воспроизводимо
	keys := make([]int, 0, len(ConditionReflexes))
	for k, v := range ConditionReflexes {
		if v == nil {
			continue
		}
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, id := range keys {
		//v := ConditionReflexes[id]
		v, ok := ReadeConditionReflexes(id)
		if !ok {
			continue
		}

		/* поиск узла дерева автоматизмов по условиям у.рефлекса
		Если нет такого узла - дорастить ветку.
		Выдать ID узла
		*/
		//actID := TriggerStimulsArr[v.lev3]
		actID, ok := ReadeTriggerStimulsArr(v.lev3)
		if !ok {
			continue
		}

		tm := psychic.GetToneMoodID(actID.ToneID, actID.MoodID) // тон-настроение
		verbalID := actID.PhraseID                              // фраза PhraseID
		// s:=wordSensor.GetPhraseStringsFromPhraseID(verbalID[0]);if len(s)>0{}
		nodeID := 0
		if verbalID != nil && len(verbalID) > 0 {
			// создать образ Брока
			FirstSimbolID := wordSensor.GetFirstSymbolFromPraseID(verbalID)
			_, verbal := psychic.CreateVerbalImage(FirstSimbolID, verbalID, actID.ToneID, actID.MoodID)
			nodeID = psychic.FindConditionsNode(v.lev1, v.lev2, actID.RSarr, tm, FirstSimbolID, verbal.ID)
		} else {
			nodeID = psychic.FindConditionsNode(v.lev1, v.lev2, actID.RSarr, tm, 0, 0)
		}

		//,,,,,,,,,,,,,,,,, для проверки
		//		psychic.SaveAllPsihicMemory() // чтобы сразу видеть какой узел возник
		/*
			lastNode:=psychic.AutomatizmTreeFromID[nodeID]; if lastNode!=nil{}
			if lastNode.PhraseID == 0{
				continue
			}
		*/
		//,,,,,,,,,,,,,,,,,
		if nodeID > 0 {
			// если есть привязанный к узлу автоматизм, то не привязывать еще
			exists := psychic.ExistsAutomatizmForThisNodeID(nodeID)
			if exists {
				//,,,,,,,,,,,,,,,,, для проверки
				//	aArr:=psychic.AutomatizmBelief2FromTreeNodeId[nodeID];if aArr!=nil{}
				count++
				continue
			}

			psychic.NoWarningCreateShow = true
			ActionsImageID, _ := psychic.CreateNewlastActionsImageID(0, 0, v.ActionIDarr, nil, 0, 0, true)
			_, autmzm := psychic.CreateAtutomatizmNoSaveFile(nodeID, ActionsImageID)
			psychic.NoWarningCreateShow = false
			if autmzm != nil {
				autmzm.Usefulness = 0                  // пока предположительно
				psychic.SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
				// ?? autmzm.GomeoIdSuccesArr какие ID гомео-параметров улучшает это действие
				count++
				newCount++
			}
		}
	}
	psychic.SaveAllPsihicMemory()
	return "Процесс нормально завершен, создано " + strconv.Itoa(newCount) + " новых автоматизмов."
}

/*
	сканировать для всех безусловных рефлексов,

создавать ветку дерева автоматизма если такой еще нет,
создавать автоматизм, прикрепляя его к нужно ветке.
*/
func RunMakeAutomatizmsFromGeneticReflexes() string {
	// проверить готовность рабочих массивов и сообщить если нет
	if GeneticReflexes == nil || len(GeneticReflexes) == 0 ||
		psychic.AutomatizmTreeFromID == nil || len(psychic.AutomatizmTreeFromID) == 0 {
		return "Еще не сформировалась оперативная память, пожалуйста перезапустите процесс через пару секунд."
	}
	var newCount = 0
	var count = 0
	// сортировка по ID чтобы тестировалось воспроизводимо
	keys := make([]int, 0, len(GeneticReflexes))
	for k, v := range GeneticReflexes {
		if v == nil {
			continue
		}
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, id := range keys {
		v := GeneticReflexes[id]
		//for _, v := range GeneticReflexes {

		//	v=GeneticReflexes[3673]
		// для проверки
		//	if count>6{
		//	psychic.SaveAllPsihicMemory();
		//	return ""; }

		/* поиск узла дерева автоматизмов по условиям у.рефлекса
		Если нет такого узла - дорастить ветку.
		Выдать  ID узла
		*/
		nodeID := psychic.FindConditionsNode(v.lev1, v.lev2, v.lev3, 0, 0, 0)
		//,,,,,,,,,,,,,,,,, для проверки
		//		psychic.SaveAllPsihicMemory() // чтобы сразу видеть какой узел возник
		/*
			lastNode:=psychic.AutomatizmTreeFromID[nodeID]; if lastNode!=nil{}
			if lastNode.PhraseID == 0{
				continue
			}
		*/
		//,,,,,,,,,,,,,,,,,
		if nodeID > 0 {
			// если есть привязанный к узлу автоматизм, то не привязывать еще
			exists := psychic.ExistsAutomatizmForThisNodeID(nodeID)
			if exists {
				//,,,,,,,,,,,,,,,,, для проверки
				//	aArr:=psychic.AutomatizmBelief2FromTreeNodeId[nodeID];if aArr!=nil{}
				count++
				continue
			}
			//  создать автоматизм и привязать его к nodeID
			psychic.NoWarningCreateShow = true
			ActionsImageID, _ := psychic.CreateNewlastActionsImageID(0, 0, v.ActionIDarr, nil, 0, 0, true)
			_, autmzm := psychic.CreateAtutomatizmNoSaveFile(nodeID, ActionsImageID)
			psychic.NoWarningCreateShow = false
			if autmzm != nil {
				autmzm.Usefulness = 0                  // пока предположительно
				psychic.SetAutomatizmBelief(autmzm, 2) // сделать автоматизм штатным
				// ?? autmzm.GomeoIdSuccesArr какие ID гомео-параметров улучшает это действие
				count++
				newCount++
			}
		}
	}
	psychic.SaveAllPsihicMemory()
	return "Процесс нормально завершен, создано " + strconv.Itoa(newCount) + " новых автоматизмов."
}

____________________________________________

BOT/brain/reflexes/condition_reflex.go
____________________________________________
/*  Условные рефлексы

Для образования условных рефлексов необходимо:
1. Совпадение во времени (сочетание) какого-либо индифферентного раздражителя (условного)
	с раздражителем, вызывающим соответствующий безусловный рефлекс (безусловный раздражитель).
2. Необходимо, чтобы действие условного раздражителя несколько предшествовало действию безусловного.
3. Условный раздражитель должен быть не вызывающим значительной самостоятельной реакции.
4. Рефлекс возникает только после нескольких повторений сочетаний 2 (news_detectior.go: if tempImg.motAutmtzmID > 2 - в При каждом запуске возникает rank int)),
это избавляет от случайных связей.
И возникаеющий образ рефлекса гасится, если долго не подтверждаются сочетания, за ночь - точно.
Условия затухания условного рефлекса:
1. Долгое отсутствие пускового стимула (узла ветки, с которого он запускается) – это легко реализуется добавлением в структуру у.рефлекса lastActivation int – в числе пульсов и времени протухания истекших рефлексов;
2. Действие конкурентных раздражителей – т.е. подавление конкурентными рефлексами и автоматизмами – т.е. если есть более значимый рефлекс или любой автоматизм на тот же пусковой стимул, то он блокирует у.рефлекс. В структуре у.рефлекса есть его ранг (число цепочки родителей), чем он выше, тем рефлекс приоритетнее среди других. Автоматизм приоритетнее любого рефлекса.
3. При отсутствии “подкрепления” после совершенного действия. Но безусловные рефлексы не угасают при этом, они безусловны и по отношению к тому, что происходит после действия и их “подкрепление” – обусловлено наследственной эволюцией: безусловные рефлексы постоянны, даны от рождения и не угасают на протяжении всей жизни.. У условных рефлексов точно та же функциональная задача, только с новыми стимулами, значит, им так же не нужно последующее подкреплением. В литературе часто путается отсутствие подкрепления с дезадаптация, а так же условные рефлексы и автоматизмы, образующиеся при осознании.
4. Разные условные рефлексы без подкрепления угасают с неодинаковой скоростью. Более "молодые" и непрочные условные рефлексы угасают быстрее, чем более "старые", прочные условно-рефлекторные связи (func conditionRexlexFound).

Условный рефлекс может образовываться на основе безусловного
или на основе имеющегося безусловного,
используя действия исходного рефлекса для новых условий.
Такие цепи рефлексов ничем не ограничены.

Формат записи - как у безусловного рефлекса, но,
в отличие от безусловного рефлексов, lev3 - только один ID образа пускового стимула типа TriggerStimulsID

РЕЗЮМЕ
1. Усл.рефлекс возникает там, где нет безусловного на основе нового стимула N,
привзяывая к нему действия того рефлекса (условного или безусловного),
которое вызывало реакцию ПОСЛЕ данного нового стимула M.
2. Но темерь если в восприятии появляется стимул N,
то вызываемый им условный рефлекс перекрывает все рефлексы более низкого уровня,
в том числе условные меньшего уровня.
Так что в структуре бесусловного рефлекса есть параметр: rank int,
который увеличивается, если реакция наследуется от условного рефлекса
и тогда рефлекс с рангом выше, перекрывает все рангом ниже.
По умолчаню у безусловных рефлексов rank равен 0.

Формат записи:
ID|lev1|lev2 через ,|lev3 типа TriggerStimulsID|ActionIDarr через ,|rank|lastActivation|birthTime

для набивки у.рефылексов Сначала (А НЕ ОДНОВРЕМЕННО!) стимул, потом рефлекс:
ПЕРЕД ПУСКОВЫМ СТИМУЛОМ НУЖНО ЗАПОСТИТЬ СЛОВО
*/

package reflexes

import (
	"BOT/lib"
	"strconv"
	"strings"
)


/* 1 - это режим - без ограничений числа повторов для формирования у.рефлекса
Он устанавливается галкой в Пульте "набивка рабочих фраз без отсеивания мусорных слов"
Работает в news_detectior.go: if tempImg.motAutmtzmID > 2 - в func updateNewsConditions(rank int)
 */
var IsUnlimitedMode=0
var NoWarningCreateCondRef = false // true - не выдавать сообщение о новом условном рефлексе

func initConditionReflex() {
	loadConditionReflexes()
	// psychic.PsychicInit()  после 4-го пульса!
}

type ConditionReflex struct {
	ID   int
	lev1 int
	lev2 []int
	// ID образа пускового стимула типа TriggerStimulsID, в отличие от безусловного рефлекса, а только один пусковой
	lev3 int
	ActionIDarr []int
	// ранг рефлекса (число цепочки родителей), чем он выше, тем рефлекс приоритетнее среди других условных
	rank int
	/* время последней активации в ДНЯХ времени жизни LifeTime
	- для отключения рефлекса при неиспользовании в течении 50 суток жизни,
	но каждое использование укрепляют рефлекс на 10 дней жизни:
	conditionRexlexFound().
	*/
	lastActivation int
	// время рождения в ДНЯХ LifeTime т.к. более "молодые" и непрочные условные рефлексы угасают быстрее, чем более "старые".
	birthTime int
}

// у-рефлексы
//var ConditionReflexes = make(map[int]*ConditionReflex)
var ConditionReflexes []*ConditionReflex  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteConditionReflexes(index int, value *ConditionReflex) {
	addConditionReflexes(index)
	ConditionReflexes[index] = value
}
func addConditionReflexes(index int){
	if index >= len(ConditionReflexes) {
		newSlice := make([]*ConditionReflex, index+1)
		copy(newSlice, ConditionReflexes)
		ConditionReflexes = newSlice
	}
}
// считывание члена
func ReadeConditionReflexes(index int) (*ConditionReflex,bool){
	if index >= len(ConditionReflexes) || ConditionReflexes[index]==nil {
		return nil,false
	}
	return ConditionReflexes[index],true
}

////////////////////////////////////////////////////////////
// у.рефлексы - по значению ConditionReflex.lev3 (ID пускового стимула )
var ConditionReflexesFrom3 = make(map[int][]*ConditionReflex)

// последний ID в массиве у-рефлексов
var lastConditionReflexID = 0
/* создание нового условного рефлекса, если такого еще нет
Детектор нового выявляет новые условия причинного (предшествовавшего имеющемуся рефлесу) стимула,
пока не приводящего к рефлексу,
в дополнение к условиям активного рефлекса (безусловного или условного)
и обрабатывает это в updateNewsConditions(
Должно уже быть не менее 2 событий образования нововго условного рефлекса
 */
func CreateNewConditionReflex(id int, lev1 int, lev2 []int, lev3 int, ActionIDarr []int, rank int,CheckUnicum bool) (int, *ConditionReflex) {
	// посмотреть, если рефлекс с такими же условиями уже есть
	if CheckUnicum {
		idOld, rOld := compareCRUnicum(lev1, lev2, lev3)
		if idOld > 0 {
			// если условия те же, но действия уже другие - подставить в существующий рефлекс новые действия
			if !lib.EqualArrs(rOld.ActionIDarr, ActionIDarr) {
				rOld.ActionIDarr = ActionIDarr
				// установить lastActivation в актуальное состояние
				rOld.lastActivation = int(LifeTime/(3600*24)) // последняя активация
				rOld.birthTime = int(LifeTime/(3600*24)) // время рождения
			}
			return idOld, rOld
		}
	}

	if id == 0 {
		lastConditionReflexID++
		id = lastConditionReflexID
	} else {
		if lastConditionReflexID < id {
			lastConditionReflexID = id
		}
	}

	var newW ConditionReflex
	newW.ID = id
	newW.lev1 = lev1
	newW.lev2 = lev2
	newW.lev3 = lev3
	newW.ActionIDarr = ActionIDarr
	newW.rank = rank
	newW.lastActivation = int(LifeTime/(3600*24)) // последняя активация
	newW.birthTime = int(LifeTime/(3600*24)) // время рождения

	//ConditionReflexes[id] = &newW
	WriteConditionReflexes(id, &newW)

	ConditionReflexesFrom3[lev3] = append(ConditionReflexesFrom3[lev3],&newW)
	if !NoWarningCreateCondRef {
		lib.WritePultConsol("Создан новый условный рефлекс.")
	}
	return id, &newW
}

// посмотреть, если условный рефлекс с такими же условиями уже есть
func compareCRUnicum(lev1 int, lev2 []int, lev3 int) (int, *ConditionReflex) {
	for k, v := range ConditionReflexes {
		if v==nil{continue}
		if v.lev1 == lev1 && lib.EqualArrs(v.lev2, lev2) && v.lev3 == lev3 {
			return k, v
		}
	}
	return 0, nil
}

// сохранить имеющиеся условные рефлексы
/* формат такой же как у безусловных (ID|lev1|lev2_1,lev2_2,...|lev3|actin_1,actin_2,...)
но отличаетсмя для lev3, который есть - только один ID образа пускового стимула типа TriggerStimulsID
*/
func SaveConditionReflex() {
	var out = ""
	for k, v := range ConditionReflexes {
		if v==nil{continue}
		out += ListConditionReflex(k, v) + "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/condition_reflexes.txt", out)
}

// Строка условного рефлекса по ID и value
func ListConditionReflex(k int, v *ConditionReflex)string {
	var out = ""

	out += strconv.Itoa(k) + "|"
	out += strconv.Itoa(v.lev1) + "|"
	for i := 0; i < len(v.lev2); i++ {
		if i > 0 { out += ","	}
		out += strconv.Itoa(v.lev2[i])
	}
	out += "|"
	out += strconv.Itoa(v.lev3) + "|"
	for i := 0; i < len(v.ActionIDarr); i++ {
		if i > 0 { out += "," }
		out += strconv.Itoa(v.ActionIDarr[i])
	}
	out += "|"
	out += strconv.Itoa(v.rank) + "|"
	out += strconv.Itoa(v.lastActivation) + "|"
	out += strconv.Itoa(v.birthTime)

	return out
}

/*  загрузить  условные рефлексы из файла в формате
ID|lev1|lev2 через ,|lev3 типа TriggerStimulsID|ActionIDarr через ,|rank|lastActivation|birthTime
 в отличие от безусловного рефлекссв, а только один ID образа пускового стимула типа TriggerStimulsID
*/
func loadConditionReflexes() {
	NoWarningCreateCondRef = true
	path := lib.GetMainPathExeFile()
	lines, _ := lib.ReadLines(path + "/memory_reflex/condition_reflexes.txt")

	ConditionReflexes = make([]*ConditionReflex, len(lines))//задать сразу имеющиеся в файле число

	for i := 0; i < len(lines); i++ {
		if len(lines[i]) < 4 { continue	}
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		lev1, _ := strconv.Atoi(p[1])
		// второй уровень
		pn := strings.Split(p[2], ",")
		var lev2 []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				lev2 = append(lev2, b)
			}
		}
		// третий уровень
		lev3, _ := strconv.Atoi(p[3])

		pn = strings.Split(p[4], ",")
		var ActionIDarr []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				ActionIDarr = append(ActionIDarr, b)
			}
		}
		rank, _ := strconv.Atoi(p[5])
		lastActivation, _ := strconv.Atoi(p[6])
		birthTime, _ := strconv.Atoi(p[7])

		_, r := CreateNewConditionReflex(id, lev1, lev2, lev3, ActionIDarr, rank,false)
		r.lastActivation = lastActivation
		r.birthTime = birthTime
	}
	NoWarningCreateCondRef=false
	return
}

/* Угас ли рефлекс или его можно использовать?
Вызывается:
1) при каждом срабатывании рефлекса
2) для проверки состояния рефлекса
В первом случае рефлекс продлевает время жизни,
во втором случае он может быть пассивирован, если время жизни превысило период его угасания
Возвращает true если рефлекс активен, - false - если рефлекс угас.
Принцип:
при каждой активации рефлекса (в conditionRexlexFound()) его время жизни продлевается
за счет перезаписывания времени рождения birthTime - уменьшая его вплоть до 0.

Если рефлекс пересоздается (его актуальность подтверждается новым сочетанием причины и следствия),
то его время жизни обновляется в func compareCRUnicum(
 */
func checkReflexLifeTime(reflex *ConditionReflex) bool {
	// рефлексы, только что созданные автоматически не проверять, они всегда новые:
	if reflex.lastActivation == 0  { // !!! только только что созданные || (reflex.lastActivation - reflex.birthTime)==0
		reflex.lastActivation = int(LifeTime / (3600 * 24)) // последняя активация
		reflex.birthTime=reflex.lastActivation
		return true
	}
	// время жизни рефлекса
	// может быть отрицательным - при неубиваемом рефлексе, см. ниже
	life:= reflex.lastActivation - reflex.birthTime
	if life > 50 { // рефлекс угас и не должен использоваться
		return false
	}
	// последняя активация
	reflex.lastActivation = int(LifeTime / (3600 * 24))
	// удлинить время жизни на 10 дней
	reflex.birthTime += 10
	/* пусть при постоянном сипользовании рефлекс станет неубиваемым
	if reflex.birthTime > reflex.lastActivation {
		reflex.birthTime = reflex.lastActivation
	}
	*/
	// SaveConditionReflex() reflex записывается при текущем сеансе сохранения памяти
	return true
}

// обновить время жизни всех рефлексов
func ClinerTimeConditionReflex() string {
	for _, v := range ConditionReflexes {
		if v==nil{continue}
		v.lastActivation = int(LifeTime / (3600 * 24)) // последняя активация
		v.birthTime = v.lastActivation // время рождения
	}
	SaveConditionReflex()
	return "Обновлено время жизни всех рефлексов"
}
//////////////////////////////////////////////////////
____________________________________________

BOT/brain/reflexes/condition_reflex_detector.go
____________________________________________
/* распознаватель условного рефлекса

1. С помощью findConditionsReflesFromPrase( из всех у.рефлексов с данным ID образа пускового стимула (imgId3)
выбирается тот, что подходит к данным условиям 1 и 2 уровня.
2. Если на публьте была вбита фраза, для которой нет imgId3, то фраза очищается от неалфавитных символов
и снова пробуется найти подходящий imgId3
3. Если все еще нет подходящего imgId3 то фраза комбинируется:
перебираются все сочетания слов до максимального числа, без перемешивания, не менее чем по 2 слова
4. Если все еще нет подходящего imgId3 то пробуются все слова фразы, не менее 5 символов.
Это позволяет найти у.рефлекс среди длинной фразы, например,
во фразе "я боюсь тебя" будет найден рефлекс на слово "боюсь".
*/

package reflexes

import (
	"BOT/brain/gomeostas"
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
	"BOT/tools"
	"strings"
)

/*
	есть ли условный рефлекс для активного узла дерева рефлексов

т.к. func conditionRexlexFound вызывается несколько раз (разное число в разных случаях) за один пульс
то нужно просто выдавать первое полученное значение.
Функция findConditionsReflesFromImgID очень тяжелая и поэтому нужно минимизировать ее вызовы.
*/
var oldCondVal []int
var conditionRexlexFoundRes = false

func conditionRexlexFound(cond []int) bool {
	if cond == nil || len(cond) == 0 {
		return false
	}
	if oldCondVal != nil && lib.EqualArrs(oldCondVal, cond) {
		return conditionRexlexFoundRes
	}
	oldCondVal = cond

	reflex := getRightConditionReflexesFrom3(cond[0])
	if reflex == nil {
		// попробовать найти схожие по образу рефлексы чтобы не так жестко привязываться к точности фразы
		reflex = findConditionsReflesFromImgID(cond, cond[0])
	}
	if reflex == nil {
		conditionRexlexFoundRes = false
		return false
	}
	res := checkReflexLifeTime(reflex)
	if res {
		conditionReflexesIdArr = append(conditionReflexesIdArr, reflex.ID)
		flgConditionReflexesIdArr = true
		conditionRexlexFoundRes = true
		return true
	}
	flgConditionReflexesIdArr = false
	conditionRexlexFoundRes = false
	return false
}

/*
	попробовать найти другие образы типа TriggerStimuls,

упрощая фразу из массива фраз TriggerStimulsArr[cond[0]].PhraseID []int
перебором массива var TriggerStimulsArr = make(map[int]*TriggerStimuls)
*/
func findConditionsReflesFromImgID(cond []int, ImgID int) *ConditionReflex {
	var reflex *ConditionReflex

	// выделить текущую фразу
	//img := TriggerStimulsArr[ImgID]
	img, ok := ReadeTriggerStimulsArr(ImgID)
	if !ok {
		return nil
	}
	if img == nil || img.PhraseID == nil {
		return nil
	}
	var prase = ""
	for i := 0; i < len(img.PhraseID); i++ {
		prase += wordSensor.GetPhraseStringsFromPhraseID(img.PhraseID[i])
	}
	prase = strings.Trim(prase, "")

	if len(prase) > 0 {
		// если есть не буквенные символы, то убрать их
		prase = wordSensor.ClinerNotAlphavit(prase)
		// есть ли такой образ?
		reflex = findConditionsReflesFromPrase(cond, prase)
		if reflex == nil { // все еще не нашли подходящий рефлекс
			// если во фразе несколько слов, то попробовать все сочетания слов фразы по порядку (а не перемещивая)
			pArr := strings.Split(prase, " ")
			var wArr []string
			for i := 0; i < len(pArr); i++ {
				if len(pArr[i]) == 0 {
					continue
				}
				wArr = append(wArr, pArr[i])
			}
			if len(wArr) > 1 {
				max := len(wArr)
				if max > 5 {
					max = 5
				} // не более 5 слов во фразе для подбора условного рефлекса
				limit := len(wArr) - 1 // максимальное число элементов в сочетании
				if limit > 3 {
					limit = 3
				}
				// найти все сочетания ряда чисел от 0 до максимального подряд, без перемешивания, не менее чем по 2 слова
				combArr := tools.GetAllCombinationsOfSeriesNumbers(len(wArr), limit)
				// перебор сочетаний слов combArr
				for i := 0; i < len(combArr); i++ {
					var words = ""
					for n := 0; n < len(combArr[i]); n++ {
						if n > 0 {
							words += " "
						}
						words += wArr[combArr[i][n]]
					}
					// есть ли такой образ?
					reflex = findConditionsReflesFromPrase(cond, words)
					if reflex != nil {
						return reflex
					}
				}
			}
			// напоследок посмотреть по одному длинному слову, > 5 символов (у русских 5*2)
			for i := 0; i < len(wArr); i++ {
				if len(wArr[i]) < 10 {
					continue
				}
				// есть ли такой образ?
				reflex = findConditionsReflesFromPrase(cond, wArr[i])
				if reflex != nil {
					return reflex
				}
				// м.б. еще и первая-последняя буквы - точно, остальные впремешку?
				// TODO
			}
		}
	}
	return reflex
}

// поиск образа у-рефлекса
func findConditionsReflesFromPrase(cond []int, prase string) *ConditionReflex {
	if len(prase) == 0 {
		return nil
	}
	// есть ли такая фраза в Дереве фраз?
	id := wordSensor.GetExistsPraseID(prase)
	if id > 0 { // id фразы есть, найти ее образ по TriggerStimulsArr
		for k, v := range TriggerStimulsArr {
			if v == nil {
				continue
			}
			if v.PhraseID == nil {
				continue
			}
			if v.PhraseID[0] == id { // есть образ с такой фразой
				reflex := getRightConditionReflexesFrom3(k)
				// есть рефлекс с таким образом
				if reflex != nil {
					return reflex
				}
			}
		}
	}
	return nil
}

// выбор наиболее близкого по условиям рефлекса из массива с данным пусковым стимулом
// var ConditionReflexesFrom3=make(map[int][]*ConditionReflex)
func getRightConditionReflexesFrom3(imgId3 int) *ConditionReflex {
	ActiveCurBaseID = gomeostas.CommonBadNormalWell
	bsIDarr := gomeostas.GetCurContextActiveIDarr()
	rArr := ConditionReflexesFrom3[imgId3]
	if rArr == nil {
		return nil
	}
	for _, v := range rArr {
		// это - способ прохода дерева без рекурсии, т.к. строго заданы уровни веток:
		if v.lev1 == ActiveCurBaseID && lib.EqualArrs(v.lev2, bsIDarr) {
			return v
		}
	}
	return nil
}

____________________________________________

BOT/brain/reflexes/condition_reflex_maker_from_list.go
____________________________________________
/*  Сформировать условные рефлексы на основе списка фраз-синонимов */

package reflexes

import (
	wordSensor "BOT/brain/words_sensor"
	"strconv"
	"strings"
)

/* Затрагивает файлы памяти:
condition_reflexes.txt
trigger_stimuls_images.txt
word_tree.txt
phrase_tree.txt

т.к. вставляются новые слова и фразы в вербальные деревья, формируются образы пусковых фраз
 */

/*  Сформировать условные рефлексы на основе списка фраз-синонимов */
func FormingConditionsRefleaxFromList(list string) string {
	// тестовый запуск в tree_activation.go readyForRecognitionRflexes()
 // list="4774|1|1,2,8||18,50,76,79|ненавижу\r\n4775|1|1,2,8|1|17,50,76,79,80|нет\r\n4777|1|1,2,8|1,4|14,17,50,76,79|ну\r\n4780|1|1,2,8|1,7|9,17,50,76,79,80|понятно\r\n4783|1|1,2,8|1,10|38,50,73,76,79,80|смеюсь"

	if len(list) < 5 { return "Пустой файл" }

	strArr := strings.Split(list, "\r\n")
	for n := 0; n < len(strArr); n++ {
		p:=strings.Split(strArr[n], "|")
		lev1, _ := strconv.Atoi(p[1])
		// второй уровень
		pn := strings.Split(p[2], ",")
		var lev2 []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				lev2 = append(lev2, b)
			}
		}
		// третий уровень - создать образ пускового стимула фразы типа TriggerStimulsID
		// засунуть фразу в дерево слов и дерево фраз
		prase := p[5]
		wordSensor.VerbalDetection(prase, 0, 0, 0)
		PhraseID := wordSensor.CurrentPhrasesIDarr
		tID, vt := CreateNewlastTriggerStimulsID(0, nil, PhraseID, 0, 0,true)
		if vt != nil{}
		SaveTriggerStimulsArr()
		lev3 := tID
		pn = strings.Split(p[4], ",")
		var ActionIDarr []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				ActionIDarr = append(ActionIDarr, b)
			}
		}
		CreateNewConditionReflex(0, lev1, lev2, lev3, ActionIDarr, 0,true)
	}
  // lib.WriteNewString(lib.GetMainPathExeFile()+"/memory_reflex/condition_reflexes.txt", out)
	SaveConditionReflex()
	return "OK"
}
____________________________________________

BOT/brain/reflexes/genetic_reflexeses.go
____________________________________________
/*
безусловные рефлексы

Формат записи в файл:
ID|BaseID|BaseStyleID[] через запятую|Triggers[] через запятую|ActionIDarr[] через запятую
*/

package reflexes

import (
	"BOT/lib"
	"sort"
	"strconv"
	"strings"
)

func init() {
	loadGeneticReflexes()
	loadImagesArrs()
	initReflexTree()
}

type GeneticReflex struct {
	ID          int
	lev1        int //BaseID
	lev2        []int // BaseStyleID[]
	lev3        []int // конпки акций с пульта
	ActionIDarr []int // действия рефлекса
	// Result int - у безусловных рефлексов нет конкуренции, кроме того, что они подавляются более высокоуровневыми рефлексами и автоматизмами
}
var GeneticReflexes = make(map[int]*GeneticReflex)

// для быстрого поиска по совпадениям строк
type geneticReflexStr struct {
ID int
lev1 string
lev2 string
lev3 string
lev4 string
actions string
}

var geneticReflexesStr = make(map[int]*geneticReflexStr)
var lastGeneticReflexID = 0

//создание нового безусловного рефлекса, если такого еще нет.
func CreateNewGeneticReflex(id int, lev1 int, lev2 []int, lev3 []int, ActionIDarr []int,CheckUnicum bool) (int, *GeneticReflex) {
	// посмотреть, если рефлекс с такими же условиями уже есть
	if CheckUnicum {
		idOld, rOld := compareUnicum(lev1, lev2, lev3)
		if idOld > 0 {
			return idOld, rOld
		}
	}

	if id == 0 {
		lastGeneticReflexID++
		id = lastGeneticReflexID
	} else {
		//		newW.ID=id
		if lastGeneticReflexID < id {
			lastGeneticReflexID = id
		}
	}

	var newW GeneticReflex
	newW.ID = id
	newW.lev1 = lev1
	newW.lev2 = lev2
	newW.lev3 = lev3
	newW.ActionIDarr = ActionIDarr
	GeneticReflexes[id] = &newW
	return id, &newW
}

// посмотреть, если рефлекс с такими же условиями уже есть
func compareUnicum(lev1 int, lev2 []int, lev3 []int) (int, *GeneticReflex) {
	for k, v := range GeneticReflexes {
		if v==nil{continue}
		if v.lev1 == lev1 && lib.EqualArrs(v.lev2, lev2) && lib.EqualArrs(v.lev3, lev3) {
			return k, v
		}
	}
	return 0, nil
}

// P.S. безусловные рефлексы создаются в редакторе и поэтому здесь нет функции их сохранения.
// а только загрузка имеющихся в формате ID|lev1|lev2_1,lev2_2,...|lev3_1,lev3_2,...|actin_1,actin_2,...:

//загрузка безусловных рефлексов из файла хранения
func loadGeneticReflexes() {
	path := lib.GetMainPathExeFile()
	lines, _ := lib.ReadLines(path + "/memory_reflex/dnk_reflexes.txt")
	for i := 0; i < len(lines); i++ {
		if len(lines[i]) < 4 {
			continue
		}
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		lev1, _ := strconv.Atoi(p[1])
		// второй уровень
		pn := strings.Split(p[2], ",")
		var lev2 []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				lev2 = append(lev2, b)
			}
		}
		// НЕ СОЗДАВАТЬ ЗАРАНЕЕ createNewBaseStyle(0, lev2,true)
		// третий уровень
		pn = strings.Split(p[3], ",")
		var lev3 []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				lev3 = append(lev3, b)
			}
		}
		// конпки акций с пульта
		pn = strings.Split(p[4], ",")
		var ActionIDarr []int
		for i := 0; i < len(pn); i++ {
			b, _ := strconv.Atoi(pn[i])
			if b > 0 {
				ActionIDarr = append(ActionIDarr, b)
			}
		}
		CreateNewGeneticReflex(id, lev1, lev2, lev3, ActionIDarr,false)
		var newS geneticReflexStr
		newS.ID=id
		newS.lev1=p[1]
		newS.lev2=p[2]
		newS.lev3=p[3]
		newS.actions=p[4]
		geneticReflexesStr[id]=&newS
	}
	return
}

/* Сохранить в файл безусловные рефлексы */
func SaveGeneticReflexes() {
	var out string

	// сохранение только в режиме Larva
	if EvolushnStage > 0 {return}

	keys := make([]int, 0, len(GeneticReflexes))
	for k,v := range GeneticReflexes {
		if v==nil{continue}
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, k := range keys {
		out += ListDnkReflex(k) + "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/dnk_reflexes.txt", out)
}

// Получить строку ДНК-рефлекса по ID
func ListDnkReflex(ID int)string{
	return strconv.Itoa(GeneticReflexes[ID].ID) + "|" +
		strconv.Itoa(GeneticReflexes[ID].lev1) + "|" +
		strings.Join(lib.StrArrToIntArr(GeneticReflexes[ID].lev2), ",") + "|" +
		strings.Join(lib.StrArrToIntArr(GeneticReflexes[ID].lev3), ",") + "|" +
		strings.Join(lib.StrArrToIntArr(GeneticReflexes[ID].ActionIDarr), ",")
}
____________________________________________

BOT/brain/reflexes/get_trigger_automatizm_tree.go
____________________________________________
/* получение информации о пусковых стимулах Дерева автоматизмов психики
Здесь - против цикличности импорта. */

package reflexes

import (
	"BOT/brain/action_sensor"
	"BOT/brain/psychic"
)

func GetTreeAutomatizmTriggersInfo(treeNodeID int) string {
	var out = ""

	//	treeNode := psychic.AutomatizmTreeFromID[treeNodeID]
	treeNode, ok := psychic.ReadeAutomatizmTreeFromID(treeNodeID)
	if ok && treeNode.ActivityID > 0 {
		out += "Действия кнопок: <b>" + GetAcivButtInfo(treeNode.ActivityID) + "</b><br>"
	}
	if treeNode.PhraseID > 0 {
		out += "Фраза: \"<b>" + psychic.GetPraseStringsFromVerbalID(treeNode.PhraseID) + "</b>" + "\"<br>"
	}
	if treeNode.ToneMoodID > 0 {
		out += "Тон-Настроение: " + psychic.GetToneMoodStrFromID(treeNode.ToneMoodID) + ""
	}

	return "<div style='font-weight:200;text-align:left'>" + out + "</div>"
}

// текстовое отображение сочетаний действия с пульта образа psychic.Activity
func GetAcivButtInfo(triggerID int) string {
	var out = ""
	//act := TriggerStimulsArr[triggerID]
	act := psychic.ActivityFromIdArr[triggerID]
	if act == nil {
		return ""
	}

	if len(act.ActID) > 0 {
		for i := 0; i < len(act.ActID); i++ {
			if i > 0 {
				out += ", "
			}
			out += action_sensor.GetActionNameFromID(act.ActID[i])
		}
	} else {
		out += "нет"
	}
	return out
}

____________________________________________

BOT/brain/reflexes/news_detectior.go
____________________________________________
/* Детектор нового условия для создания условного рефлекса
детектор нового выявляет новые условия причинного (предшествовавшего) стимула,
не приводящего к рефлексу, в дополнение к условиям активного рефлекса (безусловного или условного)
и обрабатывает это в updateNewsConditions()
*/

package reflexes

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
)

/* массив нового сочетания пусковых (Trigger) стимулов в восприятии
Накапливает образы в течение бодрствования, т.е. не сохраняет в файле.
Идентичен TriggerStimuls но с motAutmtzmID int.
если motAutmtzmID>3 - создается условный рефлекс, а TriggerStimulsTemp записывается как новый образ TriggerStimuls,
который участвует в распознавании рефлексов в дереве рефлексов.

Массивы TriggerStimulsTemp не сохраняются в памяти т.к.
стимул должен набрать 3 повторения в течение ближайшего времени
- за время бодрствования.
*/
type TriggerStimulsTemp struct {
	ID int 								// идентификатор данного сочетания пусковых стимулов
	TriggerStimulsID int 	// ID образа пускового стимула типа TriggerStimulsID
	Count int 						// число повторений до оразования рефлекса
}

var TriggerStimulsTempArr = make(map[int]*TriggerStimulsTemp)

// создать образ сочетаний пусковых стимулов
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0| - ID=2
var lastTriggerStimulsTempID = 0

func createNewlastTriggerStimulsTempID(id int, ts *TriggerStimuls) (int, *TriggerStimulsTemp) {
	oldID, oldVal := checkUnicumTriggerStimulsTemp(ts.RSarr, ts.PhraseID, ts.ToneID, ts.MoodID)
	if oldVal != nil { // рефлекс с таким условием уже есть
		return oldID, oldVal
	}
	if id == 0 {
		lastTriggerStimulsTempID++
		id = lastTriggerStimulsTempID
	} else {
		if lastTriggerStimulsTempID < id {
			lastTriggerStimulsTempID = id
		}
	}

	var node TriggerStimulsTemp
	node.ID = id
	node.TriggerStimulsID = ts.ID
	TriggerStimulsTempArr[id] = &node
	return id, &node
}
func checkUnicumTriggerStimulsTemp(bArr []int, PhraseID []int, ToneID int, MoodID int) (int, *TriggerStimulsTemp) {
	for id, v := range TriggerStimulsTempArr {
		if v==nil{continue}
		//ts := TriggerStimulsArr[v.TriggerStimulsID]
		ts,ok:=ReadeTriggerStimulsArr(v.TriggerStimulsID)
		if !ok {return 0, nil}
		if !lib.EqualArrs(bArr, ts.RSarr) { continue }
		if !lib.EqualArrs(PhraseID, ts.PhraseID) { continue }
		if ToneID != ts.ToneID || MoodID != ts.MoodID { continue }
		return id, v
	}

	return 0, nil
}

/* Детектор нового выявляет новые условия
причинного (предшествовавшего имеющемуся рефлесу) стимула, пока не приводящего к рефлексу,
в дополнение к условиям активного рефлекса (безусловного или условного).
Чтобы сделать у.рефлекс нужно чтобы в ксловиях имеющегося рефлекса появился новый признак
(пуск. стимул или слово). Т.е. перед пусковым стимулом нужно запостить слово.
 */
func updateNewsConditions(rank int) {
	// Не должен работать до рождения!
	if EvolushnStage < 1 { return }

	// была ли перед действиями Beast Причина == образ пусковых ситимулов, не приводящих к действиям
	if oldActiveCurTriggerStimulsID == 0 { return	} // нет значимых причин для поиска новизны
	if ActivedTerminalImage == nil { return	} // нет действий, которые могли быть порождены причиной
	//TriggImage := TriggerStimulsArr[oldActiveCurTriggerStimulsID]
	TriggImage,ok:=ReadeTriggerStimulsArr(oldActiveCurTriggerStimulsID)
	if !ok {return }

	_, tempImg := createNewlastTriggerStimulsTempID(0, TriggImage)

	// должно уже быть не менее 2 событий образования рефлекса
	// или включен режим IsUnlimitedMode "набивка рабочих фраз без отсеивания мусорных слов"
	if tempImg.Count > 2 || IsUnlimitedMode == 1 {
/*		if IsUnlimitedMode==1 && tempImg.Count < 3 { // искусственно добавить в счетчик
			tempImg.Count = 3
		}*/
		/* нужно создать рефлекс или если условия те же, но действия уже другие
		   - подставить в существующий рефлекс новые действия (делается в createNewConditionReflex)
		    Иначе рефлекс просто остается прежним.
		*/
		// базовыми условиями рефлекс становится текущие условия:
		lev1 := gomeostas.CommonBadNormalWell
		lev2 := gomeostas.GetCurContextActiveIDarr()
		// ранг rank наследуется от условного рефлекса, от которого берутся действия:
		// связываются предыдущий стимул с текущим ответным действием Beast: послать фразу "вот тебе!" - нажать кнопку [больно] = "вот тебе!" - плачет, пугается
		CreateNewConditionReflex(0, lev1, lev2, oldActiveCurTriggerStimulsID, ActivedTerminalImage, rank,true)
		tempImg.Count=0 // иначе при активации б-у рефлекса прочитает его Count - а там уже 3 стоит, и перепишет его с первого раза, а надо с 3.
		SaveConditionReflex()
	} else { // просто увеличить счетчик
		tempImg.Count++
	}
	/*
		// сравниваем только по пусковым стимулам ActiveCurTriggerStimulsID
		//потому как полные безусловные рефлексы всегда уже соотвествуют базовым условиям
		pCond := TriggerStimulsArr[ActiveCurTriggerStimulsID]
		reflex:=GeneticReflexes[reflexID]
		if len(pCond.RSarr)>0{
			diff:=lib.GetDifferentIntArs(pCond.RSarr, reflex.lev3)
	    if len(diff)>0{
				temp.RSarr=diff
	    }
		}
	*/
	// обнулить использованную причину
	oldActiveCurTriggerStimulsID = 0
	// дезактивировать использованный активный образа сочетаний пусковых стимулов
	ActiveCurTriggerStimulsID = 0
}
____________________________________________

BOT/brain/reflexes/perception.go
____________________________________________
/*
восприятие действий и фраз с Пульта

Все рефлекторные и автоматические активности начинаются отсюда.
_____________________________
Сначала активируется Дерево рефлексов и собираются рефлексы на выполнение, но пока не выполняются,
потом активируется Дерево автоматизмов и собираются автоматизмы на выполнение, но пока не выполняются,
если возникает ориентировочный рефлекс, то
активируется Дерево понимания (ментальных автоматизмов) и решается, что делать дальше.
если нет ориентировочного рефлекса, то
потом выполняются автоматизмы, если их нет - то рефлексы.
________________________________
Создание образов различной иерархии контексfunc ActiveFromActionтов восприятия:
BaseStyleArr - образ сочетаний активных Базовых контекстов
TriggerStimulsArr - образ сочетаний пусковых стимулов
*/

package reflexes

import (
	"BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	"BOT/brain/psychic"
	"BOT/lib"
	"strconv"
)

// Создание образов сочетаний ID действий:
func loadImagesArrs() {
	// загрузить образы сочетаний базовых стилей
	loadBaseStyleArr()
	// загрузить образы сочетаний пусковых стимулов
	loadTriggerStimulsArr()
}

// АКТИВАЦИЯ ДЕРЕВА РЕФЛЕКСОВ ПО изменению условий, действиям с Пульта или фразе с Пульта

/*
	Вид активации дерева рефлексов:

1 - изменение сочетания базовых контекстов
2 - действия с Пульта
3 - фраза с Пульта
*/
var ActivationTypeSensor = 0

// текущее восприятие ID образов
// обновляющихся при каждом событии с Пульта или достаточно сильном изменении Базовых параметров
var ActiveCurBaseID = 0           // ID Базового состояния CommonBadNormalWell
var ActiveCurBaseStyleID = 0      // ID сочетания базовых контекстов BaseStyle
var ActiveCurTriggerStimulsID = 0 // ID теущего активного образа сочетаний пусковых стимулов TriggerStimuls
/*
	предыдущий образ сочетания пусковых стимулов

используется как ПРИЧИНА последующих событий - для формирования условных рефлексов
потому как ОБНУЛЯЕТСЯ при:
1) активации дерева рефлексов, если вызвало какое-то действие
2) через 10 пульсов после записи значения - типа причина устаревает
*/
var oldActiveCurTriggerStimulsID = 0

// момент записи значения в тике Пульса
var oldActiveCurTriggerStimulsPulsCount = 0

// Сохранить предыдущий образ сочетаний пусковых стимулов
func setOldActiveCurTriggerStimulsVal(val int) {
	oldActiveCurTriggerStimulsID = val
	oldActiveCurTriggerStimulsPulsCount = ReflexPulsCount
}

/* Активация дерева рефлексов при любом изменении условий с проверкой по каждому пульсу. */
func ActiveFromConditionChange() {
	if IsSlipping { // блокировака восприятия во время сна
		return
	}
	IsPultActionThisPuls = true

	// :) это не сработает при прерывании точной останова т.к. за это время ReflexPulsCount увеличится!
	if activetedPulsCount == ReflexPulsCount { // ждет следующего пульса
		return
	}
	// очищать прежние акции с пульта при смене сочетания Базовых контекстов.
	action_sensor.DeactivationTriggers()

	activetedPulsCount = ReflexPulsCount
	ActivationTypeSensor = 1

	ActiveCurBaseID = gomeostas.CommonBadNormalWell

	// определение текущего сочетания ID Базовых контекстов
	bsIDarr := gomeostas.GetCurContextActiveIDarr()

	// создаем новый образ Базовых контекстов, если такого еще нет
	ActiveCurBaseStyleID, _ = createNewBaseStyle(0, bsIDarr, true)

	// нет пусковых стимулов с Пульта, ActiveCurTriggerStimulsID обнуляем
	//чтобы при активации деревьев было ясно, что нет Стимула с Пульта.
	ActiveCurTriggerStimulsID = 0

	// активировать дерево рефлексов
	activeReflexTree()
	//  lib.SentActionsForPult("1|WWWWWWWW") // запустить рефлекс для тестирования

	// активировать дерево автоматизмов
	psychic.WasConditionsActiveted = true
	res := psychic.SensorActivation(ActivationTypeSensor)

	if res { // блокировать выполнение рефлексов
		if len(oldReflexesIdArr) > 0 || len(geneticReflexesIdArr) > 0 {
			lib.WritePultConsol("<span style='color:red'>Рефлекс <b>заблокирован</b></span>")
		}
		action_sensor.DeactivationTriggers()
		return
	}
	// запустить рефлексы
	toRunRefleses()

	// сбросить контекст акций по кнопкам Пульта
	action_sensor.DeactivationTriggers()
	psychic.WasConditionsActiveted = false
}

/*
	активировать дерево автоматизмов ТОЛЬКО действием в main.go reflexes.ActiveFromAction()

Если кроме действий будет и фраза, то сработает func ActiveFromPhrase()
*/
func ActiveFromAction() {
	if IsSlipping { // блокировака восприятия во время сна
		return
	}

	//	activeGameMode()//Активировать Игровой режим
	IsPultActionThisPuls = true
	/* Нужно сбрасывать буфер фраз после каждого действия кнопок потому, что действие означает оценку вербального автоматизма и завершение перебора вариантов
	Сюда перенесено так как тогда будет учитываться воспитательные действия любых кнопок, а не только Поощрить/Наказать
	*/
	psychic.UsedPraseIdArr = nil

	// определение текущего сочетания ID Базовых контекстов
	bsIDarr := gomeostas.GetCurContextActiveIDarr()

	//переактивация контекстов в зависимости от эффекта стимула, сопровождающихся болью и радостью
	rSarr := action_sensor.CheckCurActions()
	if rSarr != nil {
		effect := 0
		for i := 0; i < len(rSarr); i++ {
			ef := gomeostas.GomeostazActionCommonEffectArr[rSarr[i]] // значение эффекта Боли и Радости
			val, _ := strconv.Atoi(ef)
			effect += val
		}
		if lib.Abs(effect) > 5 {
			gomeostas.ContextActiveFromStimul(effect)
		}
	}

	ActivationTypeSensor = 2
	// при воздействии кнопками Наказать, Поощрить - записывать Usefulness ранее выполненного автоматизма
	// !!! psychic.LastAutomatipmCorrection() эти кнопки могут сопровождать и фразу! нефиг такие эксклюзивы делать!

	if activetedPulsCount == ReflexPulsCount { // ждет следующего пульса
		return // :) это не сработает при прерывании точкой останова т.к. за это время ReflexPulsCount увеличится!
	}
	activetedPulsCount = ReflexPulsCount

	ActiveCurBaseID = gomeostas.CommonBadNormalWell

	// создаем новый образ Базовых контекстов, если такого еще нет
	ActiveCurBaseStyleID, _ = createNewBaseStyle(0, bsIDarr, true)

	// создаем новый образ Пусковых стимулов ActiveCurTriggerStimulsID (TriggerStimuls.ID), если такого еще нет
	CreateNewTriggerStimulsImage()

	// активировать дерево рефлексов
	activeReflexTree()

	/* Это используется для определения момента реакция оператора Пульта на действия автоматизма - для психики.
	За 20 сек г.параметры могут просто натечь и сработает ожидание реакции оператора.
	Флаг сбрасывается через пульс после запуска автоматизма.
	*/
	psychic.WasOperatorActiveted = true

	// активировать дерево автоматизмов
	res := psychic.SensorActivation(ActivationTypeSensor)
	if res { // блокировать выполнение рефлексов
		if len(oldReflexesIdArr) > 0 || len(geneticReflexesIdArr) > 0 {
			lib.WritePultConsol("<span style='color:red'>Рефлекс <b>заблокирован</b></span>")
		}
		action_sensor.DeactivationTriggers()
		return
	}

	toRunRefleses()

	// сбросить контекст акций по кнопкам Пульта
	action_sensor.DeactivationTriggers()
}

// активировать дерево фразой  reflexes.ActiveFromPhrase()
func ActiveFromPhrase() {
	if IsSlipping { // блокировака восприятия во время сна
		return
	}

	IsPultActionThisPuls = true
	//	activeGameMode()
	//если вместе с вербальным стимулом идет действие, значит оно оценивает предыдущий ответ, и надо сбрасывать UsedPraseIdArr() - см. выше, почему
	if curPultActionsArr != nil {
		psychic.UsedPraseIdArr = nil
	}

	// :) это не сработает при прерывании точной останова т.к. за это время ReflexPulsCount увеличится!
	if activetedPulsCount == ReflexPulsCount { // ждет следующего пульса
		return
	}
	activetedPulsCount = ReflexPulsCount
	ActivationTypeSensor = 3

	ActiveCurBaseID = gomeostas.CommonBadNormalWell
	// определение текущего сочетания ID Базовых контекстов
	bsIDarr := gomeostas.GetCurContextActiveIDarr()
	// создаем новый образ Базовых контекстов, если такого еще нет
	ActiveCurBaseStyleID, _ = createNewBaseStyle(0, bsIDarr, true)

	// создать новое сочетание пусковых стимулов если такого еще нет
	CreateNewTriggerStimulsImage()

	// активировать дерево рефлексов
	activeReflexTree()

	/* Это используется для определения момента реакция оператора Пульта на действия автоматизма - для психики.
	За 20 сек г.параметры могут просто натечь и сработает ожидание реакции оператора.
	Флаг сбрасывается через пульс после запуска автоматизма.
	*/
	psychic.WasOperatorActiveted = true
	// убираем активацию рефлекса бездействия, чтобы его действия не цеплялись к создаваемому автоматизму
	// иначе сутуация на 2 стадии в процессе отзеркаливания стимула и создания автоматизма:
	// посылаем "привет" - получаем ответ: "привет" + [предлагает поиграть]
	if action_sensor.CheckCurActions() == nil {
		psychic.DeleteActualRelextActon()
	}

	// активировать дерево автоматизмов
	res := psychic.SensorActivation(ActivationTypeSensor)
	if res { // блокировать выполнение рефлексов
		if len(oldReflexesIdArr) > 0 || len(geneticReflexesIdArr) > 0 {
			lib.WritePultConsol("<span style='color:red'>Рефлекс <b>заблокирован</b></span>")
		}
		action_sensor.DeactivationTriggers()
		return
	}

	toRunRefleses()

	// сбросить контекст акций по кнопкам Пульта
	action_sensor.DeactivationTriggers()
}

/* Активация Игрового режима действием или фразой
Активация Игрового режима во всех стадиях происходит автоматически при первом стимуле в контексте Поиск или Игра
сброс актвиации для 1 стадии делается в ReflexCountPuls() при превышении времени ожидания рефлекторного ответа 20 сек
сброс активации для 2,3 стадий в делается в automatizmActionsPuls() при превышении времени ожидания ответа WaitingPeriodForActionsVal
В 4 стадии это так же можно делать произвольно, при помощи IsArbitraryGameMode: True - активировать игровой режим. Этот же флаг, пока True, не дает игровому режиму погаснуть в automatizmActionsPuls()
То есть с 4 стадии просто добавляется возможность ПРОИЗВОЛЬНО активировать и дезактивировать Игровой режим в любом контексте, а его автоматическая активация по контексту так и продолжается.

Но так как SetActionFromPult() в котором происходит изменение общего состояния активируется ПЕРЕД activeGameMode(), то при первой активации игрового режиме
возможно изменение состояния, что необходимо для естественной реакции Beast.

Во всех случаях имеет место специфический механизм УДЕРЖАНИЯ ОБРАЗА ТЕКУЩЕГО СОСТОЯНИЯ - важнейшая часть функционала ОР, который необходим для корректного формирования новых у-рефлексов и автоматизмов в одном контексте,
что особенно критично в начальных стадиях - иначе запишется сумбур. А начиная с 4 стадии он нужен чтобы не писать сумбурные групповые правила, где собираются в одну группу разные по конечному эффекту цепочки.

Активировать Игровой режим в baseContextUpdate() просто по факту наличия контекстов Поиск или Игра не нужно потому, что есть еще и внутренние реакции, не имеющие никакого отношения к игре. Например рефлексы на
отсутствие реакции со стороны Оператора. Так же имеет место изменение уровней Жизненных параметров при пульсации, что изменяет контексты. Такие естественные изменения контекстов
нужно оставить, чтобы прототип был более близок к природной реализации.

func activeGameMode(){
	if psychic.IsArbitraryGameMode || gomeostas.BaseContextActive[2] || gomeostas.BaseContextActive[3]{
		if true {// отключение для тестирования
			transfer.IsPsychicGameMode = true
			psychic.LimitGroupRules++
		}
	}
}*/

/*
	создание иерархии образов контекстов условий и пусковых стимулов в виде ID образов в [3]int
	создать последовательность уровней условий в виде массива  ID последовательности ID уровней

В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0|
*/
func getConditionsArr(lev1ID int, lev2 []int, lev3 []int, PhraseID []int, ToneID int, MoodID int) []int {
	arr := make([]int, 3)
	arr[0] = lev1ID
	arr[1], _ = createNewBaseStyle(0, lev2, true)
	arr[2], _ = CreateNewlastTriggerStimulsID(0, lev3, PhraseID, ToneID, MoodID, true)
	return arr
}

// получить сохраненное (последнее активное) сочетание пусоквых стимулов-кнопок
// reflexes.GetCurPultActionsContext()
func GetCurPultActionsContext() []int {
	var ActID []int
	if ActiveCurTriggerStimulsID > 0 {
		//ActID = TriggerStimulsArr[ActiveCurTriggerStimulsID].RSarr
		node, ok := ReadeTriggerStimulsArr(ActiveCurTriggerStimulsID)
		if !ok {
			return nil
		}
		ActID = node.RSarr
	}
	return ActID
}

____________________________________________

BOT/brain/reflexes/perception_images.go
____________________________________________
/*
Образы восприятия и образы действия области рефлексов.

Обощенные образы восприятия, возникающие в теменной ассоциативной коре полностью соотвествуют воспринимаемому
и не могут меняться.
Но в лобной коре есть отражение этих образов,
с возможностью произвольно создавать любые новые из известных элементов старого.
Поэтому для области рефлексов используется TriggerStimuls,
а для области психики - ActionsImage (с меткой Answer int // 0 - объектиное действие, 1 - субъективное предположение).
Эти два вида структур локализуются по обе стороны двигательных программ
(https://scorcher.ru/optional_class/MVAP_lections.pdf)
образуя основу "зеркальной" системы подражания
(https://scorcher.ru/axiomatics/axiom_show.php?id=522).


*/

package reflexes

import (
	"BOT/brain/action_sensor"
	wordSensor "BOT/brain/words_sensor"
	"BOT/lib"
	"strconv"
	"strings"
)

/*
	Образ текущего сочетания пусковых (Trigger) стимулов в восприятии

Сочетание пусковых стимулов, включая фразы и включая тон, настроение - должны быть уникальными
чтобы только для данного сочетания мог срабатывать данный рефлекс.

Это - образ много-модеального воздействия или просто образ восприятия.

Формат: ID|RSarr через ,|PhraseID через ,|ToneID|MoodID|

Идентична ActionsImage психики.
*/
type TriggerStimuls struct {
	ID    int   // идентификатор данного сочетания пусковых стимулов
	RSarr []int // массив действий с Пульта
	// для текущего сообщения с Пусльта:
	PhraseID []int // массив фразID (DetectedUnicumPhraseID) слова каждой фразы вытаскиваются wordSensor.GetWordArrFromPhraseID(PhraseID[0])
	ToneID   int   // тон сообщения с Пульта
	MoodID   int   // настроение оператора
}

// образы сочетаний пусковых стимулов
// var TriggerStimulsArr = make(map[int]*TriggerStimuls)
var TriggerStimulsArr []*TriggerStimuls // сам массив
// var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteTriggerStimulsArr(index int, value *TriggerStimuls) {
	addTriggerStimulsArr(index)
	TriggerStimulsArr[index] = value
}
func addTriggerStimulsArr(index int) {
	if index >= len(TriggerStimulsArr) {
		newSlice := make([]*TriggerStimuls, index+1)
		copy(newSlice, TriggerStimulsArr)
		TriggerStimulsArr = newSlice
	}
}

// считывание члена
func ReadeTriggerStimulsArr(index int) (*TriggerStimuls, bool) {
	if index >= len(TriggerStimulsArr) || TriggerStimulsArr[index] == nil {
		return nil, false
	}
	return TriggerStimulsArr[index], true
}

///////////////////////////////////////////////////////////////////////

// сохранить образы сочетаний базовых стилей
func SaveBaseStyleArr() {
	var out = ""
	for k, v := range BaseStyleArr {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		for i := 0; i < len(v.BSarr); i++ {
			out += strconv.Itoa(v.BSarr[i]) + ","
		}
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/base_style_images.txt", out)
}

// загрузить образы сочетаний базовых стилей
func loadBaseStyleArr() {
	BaseStyleArr = make(map[int]*BaseStyle)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/base_style_images.txt")
	cunt := len(strArr)
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		s := strings.Split(p[1], ",")
		var BSarr []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			si, _ := strconv.Atoi(s[i])
			BSarr = append(BSarr, si)
		}
		createNewBaseStyle(id, BSarr, false)
	}
	return
}

// базовый стиль - Образ сочетаний Базовых Контекстов гомеостаза
type BaseStyle struct {
	ID    int // идентификатор данного сочетания контекстов
	BSarr []int
}

var BaseStyleArr = make(map[int]*BaseStyle)

// создать образ сочетаний активных Базовых контекстов
var lastBaseStyleID = 0

func createNewBaseStyle(id int, BSarr []int, CheckUnicum bool) (int, *BaseStyle) {
	if CheckUnicum {
		oldID, oldVal := checkUnicumBaseStyle(BSarr)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastBaseStyleID++
		id = lastBaseStyleID
	} else {
		if lastBaseStyleID < id {
			lastBaseStyleID = id
		}
	}

	var node BaseStyle
	node.ID = id
	node.BSarr = BSarr
	BaseStyleArr[id] = &node

	return id, &node
}
func checkUnicumBaseStyle(bArr []int) (int, *BaseStyle) {
	for id, v := range BaseStyleArr {
		if v == nil {
			continue
		}
		if lib.EqualArrs(bArr, v.BSarr) {
			return id, v
		}
	}
	return 0, nil
}

// создать образ сочетаний пусковых стимулов
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0| - ID=2
var lastTriggerStimulsID = 0

// Создать новый образ сочетаний пусковых стимулов
func CreateNewlastTriggerStimulsID(id int, RSarr []int, PhraseID []int, ToneID int, MoodID int, CheckUnicum bool) (int, *TriggerStimuls) {
	if CheckUnicum {
		oldID, oldVal := checkUnicumTriggerStimuls(RSarr, PhraseID, ToneID, MoodID)
		if oldVal != nil {
			return oldID, oldVal
		}
	}

	if id == 0 {
		lastTriggerStimulsID++
		id = lastTriggerStimulsID
	} else {
		if lastTriggerStimulsID < id {
			lastTriggerStimulsID = id
		}
	}

	var node TriggerStimuls
	node.ID = id
	node.RSarr = RSarr
	node.PhraseID = PhraseID
	node.ToneID = ToneID
	node.MoodID = MoodID

	//TriggerStimulsArr[id] = &node
	WriteTriggerStimulsArr(id, &node)
	return id, &node
}

// проверка наличия образа сочетаний пусковых стимулов
func checkUnicumTriggerStimuls(bArr []int, PhraseID []int, ToneID int, MoodID int) (int, *TriggerStimuls) {
	for id, v := range TriggerStimulsArr {
		if v == nil {
			continue
		}
		if !lib.EqualArrs(bArr, v.RSarr) {
			continue
		}
		if !lib.EqualArrs(PhraseID, v.PhraseID) {
			continue
		}
		if ToneID != v.ToneID || MoodID != v.MoodID {
			continue
		}
		return id, v
	}

	return 0, nil
}

// создать новое сочетание пусковых стимулов если такого еще нет
func CreateNewTriggerStimulsImage() (int, *TriggerStimuls) {
	PhraseID := wordSensor.CurrentPhrasesIDarr
	ToneID := wordSensor.DetectedTone
	MoodID := wordSensor.CurPultMood
	RSarr := action_sensor.CheckCurActions()

	setOldActiveCurTriggerStimulsVal(ActiveCurTriggerStimulsID)
	// ActiveCurTriggerStimulsID - глобальная переменная, ее нельзя использовать при инициализации :=
	activeCurTriggerStimulsID, v := CreateNewlastTriggerStimulsID(0, RSarr, PhraseID, ToneID, MoodID, true)

	// SaveTriggerStimulsArr()
	ActiveCurTriggerStimulsID = activeCurTriggerStimulsID
	return activeCurTriggerStimulsID, v
}

// сохранить образы сочетаний пусковых стимулов
// В случае отсуствия пусковых стимулов создается ID такого отсутсвия, пример такой записи: 2|||0|0|
// ID|RSarr через ,|PhraseID через ,|ToneID|MoodID|
func SaveTriggerStimulsArr() {
	var out = ""
	for k, v := range TriggerStimulsArr {
		if v == nil {
			continue
		}
		out += strconv.Itoa(k) + "|"
		for i := 0; i < len(v.RSarr); i++ {
			out += strconv.Itoa(v.RSarr[i]) + ","
		}
		out += "|"
		for i := 0; i < len(v.PhraseID); i++ {
			out += strconv.Itoa(v.PhraseID[i]) + ","
		}
		out += "|"
		out += strconv.Itoa(v.ToneID) + "|"
		out += strconv.Itoa(v.MoodID) + "|"
		out += "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/trigger_stimuls_images.txt", out)
}

// загрузить образы сочетаний пусковых стимулов
// ID|RSarr через ,|PhraseID через ,|ToneID|MoodID|
func loadTriggerStimulsArr() {

	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/trigger_stimuls_images.txt")
	cunt := len(strArr)
	//TriggerStimulsArr = make(map[int]*TriggerStimuls)
	TriggerStimulsArr = make([]*TriggerStimuls, cunt) //задать сразу имеющиеся в файле число при загрузке из файла
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])

		s := strings.Split(p[1], ",")
		var RSarr []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			si, _ := strconv.Atoi(s[i])
			RSarr = append(RSarr, si)
		}

		s = strings.Split(p[2], ",")
		var PhraseID []int
		for i := 0; i < len(s); i++ {
			if len(s[i]) == 0 {
				continue
			}
			si, _ := strconv.Atoi(s[i])
			PhraseID = append(PhraseID, si)
		}
		x, _ := strconv.Atoi(p[3])
		ToneID := x
		x, _ = strconv.Atoi(p[4])
		MoodID := x

		CreateNewlastTriggerStimulsID(id, RSarr, PhraseID, ToneID, MoodID, false)
	}
	return
}

____________________________________________

BOT/brain/reflexes/reflex_action.go
____________________________________________
/*  Выполнение действий. Выдать на Пульт акции Beast

вывести на Пульт действия Beast строкой todoAction("xcvxvxcv") с возможностью блокировки
Каждая акция - в формате: вид действия (1 - действие рефлекса, 2 - фраза) затем строка акции,
например: "1|Предлогает поиграть" или "2|Привет!"
Можно передавать неограниченную последовательность акций, разделяя их "||"
например: "1|Предлогает поиграть||2|Привет!"
*/

package reflexes

import (
	"BOT/brain/gomeostas"
	"BOT/brain/psychic"
	termineteAction "BOT/brain/terminete_action"
	"BOT/brain/transfer"
	"BOT/lib"
	"sort"
	"strconv"
)

/* Образы сочетаний действий Beast - при выполнении ТОЛЬКО рефлексов НО НЕ автоматизмов */

// последний образ сочетаний действий Beast
var ActivedTerminalImage []int // массив действий Beast
// предыдущий  образ сочетаний действий Beast
var oldActivedTerminalImage []int

// сохранить образ действий, обеспечивая сортировку ID в массиве
func UpdateActivedTerminalImage(actionsIdArr []int) {
	// сохранить предыдущий образ
	oldActivedTerminalImage = nil
	//for _, v := range ActivedTerminalImage {
	for i := 0; i < len(ActivedTerminalImage); i++ {
		oldActivedTerminalImage = append(oldActivedTerminalImage, ActivedTerminalImage[i])
	}
	// сохранить текущий образ, обеспечивая сортировку ID в массиве
	ActivedTerminalImage = nil
	sort.Ints(actionsIdArr)
	//for _, v := range actionsIdArr {
	for i := 0; i < len(actionsIdArr); i++ {
		ActivedTerminalImage = append(ActivedTerminalImage, actionsIdArr[i])
	}
	return
}

var WakeUpping = false // true - нужно проснуться

/* выдать на Пульт подряд акции одного безусловного рефлекса
Рефлексы совершаются со средней "силой" ==5 т.к. нет мозжечкового механизма оптимизации

func TerminateGeneticReflaxActions(id int){
	if id == 0 { return	}
	reflex:=GeneticReflexes[id]
	if reflex == nil { return }
	// сохранить образы сочетаний действий
	UpdateActivedTerminalImage(reflex.ActionIDarr)

	var out = "1|"
	for i := 0; i < len(reflex.ActionIDarr); i++ {
		//проснуться
		if reflex.ActionIDarr[i] == 20 { WakeUpping = true }
		if i > 0 { out += ", "}
		out += termineteAction.TerminalActonsNameFromID[reflex.ActionIDarr[i]]
	}
	todoAction(out)
	return
}*/

// выдать на Пульт подряд акции массива ID БЕЗУСЛОВНЫХ рефлексов
// reflexKind: 1-древний безусловный, 2-новый безусловный, 2-условный
func TerminateGeneticAllReflaxActions(reflexesIdArr []int, reflexKind int) {
	if reflexesIdArr == nil {
		return
	}
	// очистить буфер передачи действий на пульт
	// lib.ActionsForPultStr = ""
	lastActivnostFromPult = ReflexPulsCount // новый период 10 секундного ослеживания

	// оставить только уникальные действия рефлексов и выдать такой список
	var unicumArr = make(map[int]int)
	// var rIdArr=make(map[int]int)// привязать действие к ID рефлекса
	for i := 0; i < len(reflexesIdArr); i++ {
		reflex := GeneticReflexes[reflexesIdArr[i]]
		if reflex != nil {
			for j := 0; j < len(reflex.ActionIDarr); j++ {
				unicumArr[reflex.ActionIDarr[j]] = reflexesIdArr[i]
			}
		}
	}
	if len(unicumArr) == 0 {
		return
	}
	var out = "1|<b>БЕССМЫСЛЕННЫЙ безусловный рефлекс:</b><br>"
	var n = 0
	var aImage []int
	for k, rID := range unicumArr {
		// проснуться
		if k == 20 {
			WakeUpping = true
		}
		if n > 0 {
			out += ", "
		}
		ta := termineteAction.TerminalActonsNameFromID[k]
		rIDstr := strconv.Itoa(rID)
		out += "<img src=\"/img/edit.png\" style=\"cursor:pointer;\" onClick=\"edit_b_reflex(" + rIDstr + ")\" title=\"Изменить действия рефлекса ID=" + rIDstr + "\">&nbsp;" + ta
		// при этом меняются гомео-параметры:
		ExpensesGomeostatParametersAfterAction(k)
		aImage = append(aImage, k)
		n++
	}
	// сохранить образы сочетаний действий
	UpdateActivedTerminalImage(aImage)
	// детектор нового вычленяет новые условия по сравненеию с условиями рефлекса и обрабатывает это:
	// здесь ВСЕГДА rank=0 т.к. тут обрабатываются только безусловные
	updateNewsConditions(0)
	todoAction(out)
	// очистить массивы рефлексов данного типа
	switch reflexKind {
	case 1:
		oldReflexesIdArr = nil
	case 2:
		geneticReflexesIdArr = nil
		// здесь не бывает	case 3: conditionReflexesIdArr=nil
	}

	return
}

// выдать на Пульт подряд акции массива ID УСЛОВНЫХ рефлексов
func TerminateConditionAllReflaxActions(reflexesIdArr []int) {
	if reflexesIdArr == nil || len(reflexesIdArr) == 0 {
		return
	}
	var out = "1|<b>БЕССМЫСЛЕННЫЙ условный рефлекс:</b><br>"

	// может быть только один член в reflexesIdArr
	//reflex := ConditionReflexes[reflexesIdArr[0]]
	reflex, ok := ReadeConditionReflexes(reflexesIdArr[0])
	if !ok {
		return
	}
	var n = 0
	var aImage []int
	// здесь не может быть неуникальных ID действий, так что просто:
	for i := 0; i < len(reflex.ActionIDarr); i++ {
		k := reflex.ActionIDarr[i]
		//проснуться
		if k == 20 {
			WakeUpping = true
		}
		if n > 0 {
			out += ", "
		}
		out += termineteAction.TerminalActonsNameFromID[k]
		// при этом меняются гомео-параметры:
		ExpensesGomeostatParametersAfterAction(k)
		aImage = append(aImage, k)
		n++
	}
	// сохранить образы сочетаний действий
	UpdateActivedTerminalImage(aImage)
	// детектор нового вычленяет новые условия по сравненеию с условиями рефлекса и обрабатывает это:
	// для создания условных рефлексов более высого ранга на основе усл.рефлексов
	//rank := ConditionReflexes[reflex.ID].rank
	node, ok := ReadeConditionReflexes(reflex.ID)
	if ok {
		rank := node.rank
		updateNewsConditions(rank + 1)
		todoAction(out)
		conditionReflexesIdArr = nil
		flgConditionReflexesIdArr = false
	}
}

// выдать на Пульт подряд акции массива по каждому рефлексу отдельной строкой действий
/*
func TerminateGeneticEachReflaxActions(reflexesIdArr []int){
	if reflexesIdArr==nil{
		return
	}
	var out=""
	for i := 0; i < len(reflexesIdArr); i++ {
		if i>0{out+="||"} // пошел следующий рефлекс
		reflex:=GeneticReflexes[reflexesIdArr[i]]
		if reflex==nil{return}
		out+="1|"
		for j := 0; j < len(reflex.ActionIDarr); j++ {
			if j>0{out +=", "}
			out += TerminalActonsNameFromID[reflex.ActionIDarr[j]]
		}
	}
	todoAction(out)
}
*/

/*
	изменение гомео-параметров при действии

для рефлексов - нет соррекции по силе, она средняя =5
*/
func ExpensesGomeostatParametersAfterAction(actID int) {
	if transfer.IsPsychicGameMode {
		return // не воздействовать на гомео-параметры в игровом режиме
	}
	if gomeostas.NotAllowSetGomeostazParams {
		return
	}
	se := termineteAction.TerminalActionsExpensesFromID[actID]
	if se != nil {
		for j := 0; j < len(se); j++ {
			gomeostas.GomeostazParams[se[j].GomeoID] += se[j].Diff
			if gomeostas.GomeostazParams[se[j].GomeoID] > 100 {
				gomeostas.GomeostazParams[se[j].GomeoID] = 100
			}
			if gomeostas.GomeostazParams[se[j].GomeoID] < 0 {
				gomeostas.GomeostazParams[se[j].GomeoID] = 0
			}
		}
	}
}

// совершение действий и возможность их блокировки
func todoAction(out string) {
	if !psychic.GetAllowReflexRuning() {
		return
	}
	// блокировка во время сна
	if IsBlockingMotorsAction() {
		return
	}

	lib.SentActionsForPult(out)
}

/* Блокировка рефлексорных действий из Психики или во сне */
func IsBlockingMotorsAction() bool {
	notAllow1 := psychic.NotAllowReflexesAction()
	if notAllow1 || IsSlipping {
		return true
	}
	return false
}

// запустить готовые к выполнению рефлексы
func toRunRefleses() {
	if !psychic.GetAllowReflexRuning() {
		lib.WritePultConsol("<span style='color:red'>Рефлекс <b>заблокирован</b></span>")
		return
	}
	// очистить буфер передачи действий на пульт
	// lib.ActionsForPultStr = ""
	lastActivnostFromPult = ReflexPulsCount // сбросить отчет времени бездействия
	if len(conditionReflexesIdArr) > 0 {    // есть условные рефлексы
		// удалить более низкоуровневые рефлексы
		geneticReflexesIdArr = nil
		oldReflexesIdArr = nil
		// выдать на пульт действия
		conditionReflexesIdArr = lib.UniqueArr(conditionReflexesIdArr)
		TerminateConditionAllReflaxActions(conditionReflexesIdArr)
		// ОБНУЛЯЕТСЯ при активации дерева рефлексов, если вызвало какое-то действие
		oldActiveCurTriggerStimulsID = 0
		return
	}
	if len(geneticReflexesIdArr) > 0 { // есть новые безусловные рефлексы
		NoUnconditionRefles = ""
		// выдать на пульт действия
		geneticReflexesIdArr = lib.UniqueArr(geneticReflexesIdArr)
		TerminateGeneticAllReflaxActions(geneticReflexesIdArr, 2)
		// В TerminateGeneticAllReflaxActions детектор нового updateNewsConditions() вычленяет новые условия по сравненеию с условиями рефлекса и обрабатывает это:
		// ОБНУЛЯЕТСЯ при активации дерева рефлексов, если вызвало какое-то действие
		oldActiveCurTriggerStimulsID = 0
		return
	}
	if len(oldReflexesIdArr) > 0 { // есть старые безусловные рефлексы
		// выдать на пульт действия
		oldReflexesIdArr = lib.UniqueArr(oldReflexesIdArr)
		TerminateGeneticAllReflaxActions(oldReflexesIdArr, 1)
		// В TerminateGeneticAllReflaxActions детектор нового updateNewsConditions() вычленяет новые условия по сравненеию с условиями рефлекса и обрабатывает это:
		//ОБНУЛЯЕТСЯ при активации дерева рефлексов, если вызвало какое-то действие
		oldActiveCurTriggerStimulsID = 0
		return
	}
	/* если нет условного и безусловного рефлекса, то совершается самый простейший безусловный рефлекс
	по сочетаниям редактора http://go/pages/terminal_actions.php
	Данный редактор связывает действие с тем, какие гомео-параметры улучшает данное действие.
	*/
	findAndExecuteSimpeReflex()
}

//////////////////////////////////////////////////////////

____________________________________________

BOT/brain/reflexes/simple_reflex.go
____________________________________________
/*
самый простейший безусловный рефлекс
по сочетаниям редактора http://go/pages/terminal_actions.php
Данный редактор связывает действие с тем, какие гомео-параметры улучшает данное действие.
Это внутренний рефлекс самоадаптации, выполняемый, если нет пусковых стимулов для более высокоуровневых рефлексов
Или рефлекс по умолчанию, что означает - реагирование будет всегда хотя бы на таком уровне
*/

package reflexes

import (
	TerminalActions "BOT/brain/terminete_action"
	"BOT/lib"
)

// найти и выполнить простейший безусловный рефлекс
func findAndExecuteSimpeReflex() {
	// только если нет другого рефлекса
	if len(lib.ActionsForPultStr) > 0 { return }
	_, actID,_ := TerminalActions.ChooseSimpleReflexexAction()
	if actID > 0 { // совершить это действие
		// очистить буфер передачи действий на пульт
		// lib.ActionsForPultStr = ""
		actStr := "0|" + TerminalActions.TerminalActonsNameFromID[actID]
		lib.SentActionsForPult(actStr)
	}
}
____________________________________________

BOT/brain/reflexes/tree.go
____________________________________________
/*
дерево рефлексов, безусловных и условных

Узлы дерева создаются только рефлексами:
безусловными сразу при загрузке, условными по мере возникновения новых - в новых уловиях.
У дерева - только 3 уровня для безусловных рефлексов, все узла которых - в виде ID:
1 - базовое состояние - ID Плохо, Норма, хорошо
2 - сочетаний активных Базовых контекстов - ID BaseStyleArr
3 - сочетаний пусковых стимулов - ID TriggerStimulsArr
При возникновении условных рефлексов просто добавляется новый узел
- образ новых условий (2 или 3-го уровней), запускающих усл.рефлекс.

Дерево обязательно должно иметь три базовых состояния вначале - без рефлексов:
1|0|1|0|0|0|0
2|0|2|0|0|0|0
3|0|3|0|0|0|0

Формат записи безусловного рефлекса: ID|baseID|styleID...|actionID...
*/

package reflexes

import (
	"BOT/lib"
	"strconv"
	"strings"
)

func initReflexTree() { // после инициализации loadGeneticReflexes()
	// tools.GetAllCombinationsOfSeriesNumbers(5,3)
	loadReflexTree()
	if len(ReflexTree.Children) == 0 { // еще нет никаких веток
		// создать первые три ветки базовых состояний
		createBasicReflexTree()
	}
	// SaveReflexesAttributes()
	readyForRecognitionRflexes() // ини для дерева распознавания рефлексов
}

/* Структура дерева рефлексов
ID|parentID|baseID|styleID|actionID|GeneticReflexID|ConditionedReflex */
type ReflexNode struct { // узел дерева рефлексов
	ID int
	baseID int 		// базовое состояние
	StyleID int 	// стиль поведения - сочетание активностей Базовых контекстов  - ID BaseStyleArr
	ActionID int 	// сочетание пусковых стимулов  - ID TriggerStimulsArr

	GeneticReflexID int 		// безусловный рефлекс
	ConditionedReflex int 	// условный рефлекс, если есть, блокирует GeneticReflexID узла

	Children []ReflexNode 	// дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID int     				// ID родителя
	ParentNode *ReflexNode  // адрес родителя
}

var ReflexTree ReflexNode // дерево рефлексов
//var ReflexTreeFromID = make(map[int]*ReflexNode) // карта для поиска рефлексов
var ReflexTreeFromID []*ReflexNode  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteReflexTreeFromID(index int, value *ReflexNode) {
	addReflexTreeFromID(index)
	ReflexTreeFromID[index] = value
}
func addReflexTreeFromID(index int){
	if index >= len(ReflexTreeFromID) {
		newSlice := make([]*ReflexNode, index+1)
		copy(newSlice, ReflexTreeFromID)
		ReflexTreeFromID = newSlice
	}
}
// считывание члена
func ReadeReflexTreeFromID(index int) (*ReflexNode,bool){
	if index >= len(ReflexTreeFromID) || ReflexTreeFromID[index]==nil {
		return nil,false
	}
	return ReflexTreeFromID[index],true
}
////////////////////////////////////////////////////
/* запрет показа карты на пульте (func GetReflexesTreeForPult()) при обновлении
против паники типа "одновременная запись и считывание карты"
Использовать для всех операций записи узлов дерева
 */
var notAllowScanInReflexesThisTime = false

var lastReflexNodeID = 0 // последний узео в дереве автоматизмов

// Создать новый узел дерева рефлексов
func createNewReflexNode(parent *ReflexNode, id int, baseID int, StyleID int,
				ActionID int, GeneticReflexID int, ConditionedReflex int,CheckUnicum bool)(int, *ReflexNode) {
	// если есть такой узел, то не создавать
	if CheckUnicum {
		idOld, nodeOld := FindReflexTreeNodeFromCondition(baseID, StyleID, ActionID)
		if idOld > 0 { return idOld,nodeOld }
	}

	if id == 0 {
		lastReflexNodeID++
		id = lastReflexNodeID
	} else {
		if lastReflexNodeID < id {
			lastReflexNodeID = id
		}
	}

	var node ReflexNode
	node.ID = id
	node.ParentNode = parent
	node.ParentID = parent.ID
	node.baseID = baseID
	node.StyleID = StyleID
	node.ActionID = ActionID
	node.GeneticReflexID = GeneticReflexID
	node.ConditionedReflex = ConditionedReflex

	parent.Children = append(parent.Children, node)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *ReflexNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
		}
	}
	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в ReflexTreeFromID:
	updateReflexTreeFromID(parent) // здесь потому, что при загрузке из файла нужно на лету получать адреса

	return id, newN
}

// корректируем адреса всех узлов
func updateReflexTreeFromID(parent *ReflexNode) {
	// updatingPhraseTreeFromID(&VernikePhraseTree)
	updatingPhraseTreeFromID(parent)
}

// проход всего дерева
func updatingPhraseTreeFromID(rt *ReflexNode) {
	if rt.ID > 0 {
		//rt.ParentNode = ReflexTreeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		node,ok:=ReadeReflexTreeFromID(rt.ParentID)
		if ok {
			rt.ParentNode = node
			//ReflexTreeFromID[rt.ID] = rt
			WriteReflexTreeFromID(rt.ID, rt)
		}
	}
	// конец ветки
	if rt.Children == nil { return }
	for i := 0; i < len(rt.Children); i++ {
		updatingPhraseTreeFromID(&rt.Children[i])
	}
}

// загрузить записанное дерево
// ID|parentID|baseID|styleID|actionID|geneticReflexID|conditionedReflex|
func loadReflexTree(){
	createNulLevelReflexTree(&ReflexTree)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/reflex_tree.txt")
	cunt := len(strArr)
	// просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 { continue	}
		if len(strArr[n]) < 2 {
			panic("Сбой загрузки дерева рефлексов: ["+strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		parentID, _ := strconv.Atoi(p[1])
		baseID, _ := strconv.Atoi(p[2])
		styleID, _ := strconv.Atoi(p[3])
		actionID, _ := strconv.Atoi(p[4])
		geneticReflexID, _ := strconv.Atoi(p[5])
		conditionedReflex, _ := strconv.Atoi(p[6])
		// новый узел с каждой строкой из файла
		node,ok:=ReadeReflexTreeFromID(parentID)
		if ok {
			createNewReflexNode(node, id, baseID, styleID,
				actionID, geneticReflexID, conditionedReflex, false)
		}
	}
	return
}

// создать первый, нулевой уровень дерева
func createNulLevelReflexTree(rt *ReflexNode) {
	rt.ID = 0
	//ReflexTreeFromID[rt.ID] = rt
	WriteReflexTreeFromID(rt.ID, rt)
	return
}

// создать первые три ветки базовых состояний
func createBasicReflexTree() {
	notAllowScanInReflexesThisTime = true // запрет показа карты при обновлении
	createNewReflexNode(&ReflexTree,0,1,0,0,0,0,false)
	createNewReflexNode(&ReflexTree,0,2,0,0,0,0,false)
	createNewReflexNode(&ReflexTree,0,3,0,0,0,0,false)
	saveReflexTree()
	notAllowScanInReflexesThisTime = false // запрет показа карты при обновлении
	return
}

// сохранить дерево рефлексов
func saveReflexTree() {
	var out = ""
	cnt := len(ReflexTree.Children)
	for n := 0; n < cnt; n++ {
		out += getReflexNode(&ReflexTree.Children[n])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_reflex/reflex_tree.txt", out)
	return
}

// получить ветку дерева в виде строки
func getReflexNode(wt *ReflexNode) string {
	var out = ""

	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|"
	out += strconv.Itoa(wt.baseID) + "|"
	out += strconv.Itoa(wt.StyleID) + "|"
	out += strconv.Itoa(wt.ActionID) + "|"
	out += strconv.Itoa(wt.GeneticReflexID) + "|"
	out += strconv.Itoa(wt.ConditionedReflex)
	out += "\r\n"

	if wt.Children == nil { return out } // конец
	for n := 0; n < len(wt.Children); n++ {
		out += getReflexNode(&wt.Children[n])
	}
	return out
}

var detectedLastNodID = 0 // текущий последний распознанный узел дерева - результат распознавания

func ConditionsDetection(condArr []int) {
	detectedLastNodID = 0 /* распознавание условий в дереве рефлексов, нахождение ветки с данными условиями данного рефлекса
	condArr получать с помощью func getConditionsArr(lev1ID int, lev2 []int, lev3 []int, PhraseID []int,ToneID int,MoodID int )([3]int){
	*/
	// основа дерева
	cnt := len(ReflexTree.Children)
	for n := 0; n < cnt; n++ {
		node := ReflexTree.Children[n]
		lev1 := node.baseID
		if condArr[0] == lev1 {
			detectedLastNodID = node.ID
			ost := condArr[1:]
			getReflexTreeNode(1, ost, &node)
			break // только один из Базовых состояний
		}
	}
	return
}

// получить последний узел ветки - результат распознавания
func getReflexTreeNode(level int, cond []int, node *ReflexNode) {
	if len(cond) == 0 { return }

	ost := cond[1:]
	for n := 0; n < len(node.Children); n++ {
		cld := node.Children[n]
		var levID = 0
		switch level {
		case 1: levID = cld.StyleID
		case 2: levID = cld.ActionID
		}
		// cond[0] потому, что на следующем уровне cond уже подрезана
		if cond[0] != levID {	return } // пошло не туда

		detectedLastNodID = node.ID
		level++
		getReflexTreeNode(level, ost, &cld)
	}
	return
}

// создание новой ветки с новым рефлексом типа GeneticReflex, начиная с заданного узла
func createNewReflexToTreeFromNodes(level int,cond []int,node *ReflexNode) int {
	if node == nil { return 0	}
	if level >= len(cond) {	return node.ID }
	var id=0
	/* switch(level){
	case 0:
		id,_=createNewReflexNode(node,0,cond[0],0,0,0,0)
	case 1:
		id,_=createNewReflexNode(node,0,cond[0],cond[1],0,0,0)
	case 2:
		id,_=createNewReflexNode(node,0,cond[0],cond[1],cond[2],0,0)
	}
	*/
	id, _ = createNewReflexNode(node,0, cond[0], cond[1], cond[2],0,0,true)
	level++
	node,ok:=ReadeReflexTreeFromID(id)
	if ok {
		id = createNewReflexToTreeFromNodes(level, cond, node)
		return id
	}
	return 0
}

/* Сразу создать и добавить безусловный рефлекс в дерево, если таких узлов еще нет.
Формат записи безусловного рефлекса: ID|baseID|styleID...|actionID...
Если у рефлекса пропущены условия, то этот рефлекс нужно привязать ко всем узлам пропущенного уровня.
 */
func addGeneticReflexesToTree(detectedActiveLastNodID int, condArr []int) {
	notAllowScanInReflexesThisTime = true // запрет показа карты при обновлении

	// найти ID GeneticReflexes (список всех dnk_reflexes.txt) по условиям
	reflexID := findGeneticReflexFromCondinion(strconv.Itoa(condArr[0]), condArr[1], condArr[2])
	if reflexID > 0 {
		// v := GeneticReflexes[reflexID]
		// trigger:=v.ActionIDarr
		level := getLevelFromNodeID(detectedActiveLastNodID)
		lastNodeID := formingBranch(reflexID, detectedActiveLastNodID, level, condArr)
		detectedActiveLastNodID = lastNodeID

		node,ok:=ReadeReflexTreeFromID(detectedActiveLastNodID)
		if ok {
			if node.GeneticReflexID > 0 {
				if condArr[2] == 0 { // древний рефлекс
					oldReflexesIdArr = append(oldReflexesIdArr, node.GeneticReflexID)
				} else { // нормальный безусловный рефлекс (с пусковым стимулом)
					geneticReflexesIdArr = append(geneticReflexesIdArr, node.GeneticReflexID)
				}
			}
		}
	}
	// сохранение  - учти, что срабатывает - после пятого пульса
	SaveReflexesAttributes()
	notAllowScanInReflexesThisTime = false
}

// найти ID GeneticReflexes (список всех dnk_reflexes.txt) по условиям
func findGeneticReflexFromCondinion(basic string,img1id int,img2id int) int {
	img1 := BaseStyleArr[img1id]
	var img2 *TriggerStimuls

	if img2id > 0 {
		//img2 = TriggerStimulsArr[img2id]
		node,ok:=ReadeTriggerStimulsArr(img2id)
		if !ok {
			return 0
		}else{
			img2=node
		}

	}
	lev1str := ""
	for i := 0; i < len(img1.BSarr); i++ {
		if len(lev1str) > 0 { lev1str += "," }
		lev1str += strconv.Itoa(img1.BSarr[i])
	}
	lev2str := ""
	if img2 != nil {
		for i := 0; i < len(img2.RSarr); i++ {
			if len(lev2str) > 0 { lev2str += "," }
			lev2str += strconv.Itoa(img2.RSarr[i])
		}
	}

	for id, v:= range geneticReflexesStr {
		if v==nil{continue}
		if v.lev1 == basic && v.lev2 == lev1str && v.lev3 == lev2str { return id }
	}
	return 0
}

// нарастить ветку недостающим узлом
func formingBranch(reflexID int,fromID int,lastLevel int,condArr []int) int {
	//lastNode := ReflexTreeFromID[fromID]
	lastNode,ok:=ReadeReflexTreeFromID(fromID)
	if ok {
		lastNodeID := createNewReflexToTreeFromNodes(lastLevel, condArr, lastNode)
		// родителем должен быть последний найденный узел, а не тот, что будет создан первым
		// !!! НЕТ !!! ReflexTreeFromID[lastNodeID].ParentID=lastNode.ID
		// привязать рефлекс
		ReflexTreeFromID[lastNodeID].GeneticReflexID = reflexID

		return lastNodeID
	}
	return 0
}

// найти уровень вложения данного узла в ветке
func getLevelFromNodeID(nodeID int) int {
	//lastNode := ReflexTreeFromID[nodeID]
	lastNode,ok:=ReadeReflexTreeFromID(nodeID)
	if ok {
		var level = 0
		for lastNode.ParentNode != nil {
			level++
			lastNode = lastNode.ParentNode
		}
		return level
	}
	return 0
}

// найти КОНЕЧНЫЙ узел по условиям
func FindReflexTreeNodeFromCondition(baseID int, StyleID int, ActionID int)(int, *ReflexNode) {
	for k, v := range ReflexTreeFromID {
		if v==nil{continue}
		if v.baseID == baseID && v.StyleID == StyleID && v.ActionID == ActionID { return k, v	}
	}
	return 0, nil
}

// сохранение при выходе reflexes.SaveReflexesAttributes() и когда нужно
// !!! но только после того, как все данные будут загружены:
func SaveReflexesAttributes() {
	if ReflexPulsCount < 5 { return	}
	// сохранить образы восприятия и пусковых стимулов после прохода всех безусловных рефлексов
	SaveBaseStyleArr()
	SaveTriggerStimulsArr()
	saveReflexTree()
	SaveConditionReflex()
	SaveGeneticReflexes()
}
____________________________________________

BOT/brain/reflexes/tree_activation.go
____________________________________________
/* активация дерева рефлексов изменением:
текущих условий, 
действиями с Пульта 
и фразой с Пульта
функциями из perception.go
ActiveFromConditionChange()
ActiveFromAction()
ActiveFromPhrase()

Здесь отрабатываются четыре вида рефлексов в следующей иерархической последовательности:
1. Условные рефлексы - на основе предыдущих безусловных (но не древних, а полных) или условных - связанных с новыми стимулами. Собираются в conditionReflexesIdArr
Каждый последующий вид рефлексов имеет приоритет над предыдущими, т.е. те не выполняются, если есть приоритетный.
2. Новые безусловные - с прописанными пусковыми стимулами. Собираются в geneticReflexesIdArr
3. Древние безусловные - у которых в условиях не прописаны пусковые стимулы. Собираются в oldReflexesIdArr
4. Древнейшие безусловные - с прошитой генетической целью (какие ID гомео-параметров улучшает действие рефлекса из http://go/pages/terminal_actions.php).
Активируется как действие по умолчанию при отсутствии всех вышеуказанных типов рефлексов.
*/

package reflexes

import (
	"BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	"BOT/brain/psychic"
	"BOT/brain/sleep"
	termineteAction "BOT/brain/terminete_action"
	"BOT/lib"
	"sort"
	"strconv"
)

/*
запрет показа карты при обновлении против паники типа "одновременная запись и считывание карты"
Использовать для всех операций записи узлов дерева
func xxxxxx(){
notAllowScanInReflexesThisTime=true
ОПЕРАЦИИ ...
notAllowScanInReflexesThisTime=false
}
*/

func readyForRecognitionRflexes() { // init() для дерева распознавания рефлексов
/*
		ActiveCurBaseID =1
		ActiveCurBaseStyleID=22 //14
		ActiveCurTriggerStimulsID =3
			activeReflexTree()
	}
 */
	initConditionReflex()
//	FormingConditionsRefleaxFromList("")
}

//для частичного распознавания нужен массив текущих активных Базовых контекстов
var curBaseCondArr []int
// массив текущих пусковых стимулов
var curPultActionsArr []int

var detectedActiveLastNodID=0
/* распознавание идет строго по совпадающим веткам, без ветвлений.
Но на каждом уровне смотрятся ветвления дочек - для нахождения не точного соотвествия.
Например, если рефлексы имеют только один Базовый контекст, а текущее состояние Beast - сочетание нескольких,
то в результате должны сработать все рефлексы, для каждой цифры в сочетании образа текущих условий.
Но если в рефлексе заданы несколько условий и такой образ точно совпадент с текущим образом условий,
то именно этот рефлекс и сработает.
*/
var detectedActiveLevel=0 // уровень условий, до которого дошло распознавание в дереве

// Собираются рефлексы, подходящие для текущих оразов условий:

var oldReflexesIdArr []int 				// собираются Древние безусловные - у которых в условиях не прописаны пусковые стимулы.
var geneticReflexesIdArr []int 		// собираются Новые безусловные - с прописанными пусковыми стимулами.
var conditionReflexesIdArr []int 	// собираются Условные рефлексы - на основе предыдущих безусловных или условных - связанных с новыми стимулами.
var flgConditionReflexesIdArr bool // true - есть у-рефлексы. Только для getPurposeGeneticAndRunAutomatizm(), чтобы не ссылаться на срез conditionReflexesIdArr[]

//  сообщить на Пульт, что при данных условиях нет б.рефлекса.
var NoUnconditionRefles = ""

// активация дерева рефлексов
func activeReflexTree() {
	detectedActiveLastNodID = 0
	detectedActiveLevel = 0
	flgConditionReflexesIdArr=false
	clinerReflexArrID()

	quardingSleepCenter()//сторожевой центр сна
	if sleep.IsSleeping {
		return// не рефлексировать во сне
	}

	// для частичного распознавания нужен массив текущих активных Базовых контекстов
	curBaseCondArr = gomeostas.GetCurContextActiveIDarr()
	if curBaseCondArr == nil{ // еще не определились актичные Базовые контексты
		return
	}
	// массив текущих пусковых стимулов
	curPultActionsArr = action_sensor.CheckCurActions()
	// для условныз реф-в
	//=GetActiveContextInfo()

	// ЗАПАРИВАЕТ lib.WritePultConsol("Активация дерева рефлексов.")
	// вытащить 3 уровня условий в виде ID их образов
	condArr := getActiveConditionsArr(ActiveCurBaseID, ActiveCurBaseStyleID, ActiveCurTriggerStimulsID)
	// ActiveCurTriggerStimulsID=1; condArr:=[]int{1, 2, ActiveCurTriggerStimulsID} // проверка подстановкой произвольных сочетаний

	// основа дерева
	cnt := len(ReflexTree.Children)
	for n := 0; n < cnt; n++ {
		node := ReflexTree.Children[n]
		lev1 := node.baseID
		if condArr[0] == lev1 {
			detectedActiveLastNodID = node.ID
			ost := condArr[1:]
			detectedActiveLevel = 1
			findReflexesNodes(detectedActiveLevel, ost, &node,1)
			//findReflexesNodes(1,condArr, &node,1)
			break // только один из Базовых состояний
		}
	}

	// результат поиска:
	if detectedActiveLastNodID > 0 { // найден узел
	// если есть условный рефлекс, то погасить б.рефлексы
		if len(conditionReflexesIdArr) > 0 {
			// удалить более низкоуровневые рефлексы
			geneticReflexesIdArr = nil
			oldReflexesIdArr = nil
		} else {
			// единственная реакция 9. Игнорирует? - для диалога Пульта чтобы сформировать рефлекс
			isIgnor := checkIgnorOnly(oldReflexesIdArr, geneticReflexesIdArr)
			// нет старых или новых безусловных рефлексов для текущих условий и если игнорирует
			if (len(oldReflexesIdArr) == 0 && len(geneticReflexesIdArr) == 0) || isIgnor {
				/* если в GeneticReflexes (список всех dnk_reflexes.txt) есть совпадающее условие,
				то создать узел дерева
				*/
				addGeneticReflexesToTree(detectedActiveLastNodID, condArr)

				// сообщить на Пульт, что при данных условиях нет б.рефлекса.
					if EvolushnStage == 0 { // только для стадии безусловных рефлексов
						if isIgnor {
							NoUnconditionRefles = "IGNORED" + GetCurrentConditionsStr() // СТРОКА УСЛОВИЙ ДЛЯ РЕФЛЕКСА
						} else {
							NoUnconditionRefles = "NOREFLEX" + GetCurrentConditionsStr() // СТРОКА УСЛОВИЙ ДЛЯ РЕФЛЕКСА
						}
						return
					}
			}
			// в консоль:
			consol := "Попытка запуска РЕФЛЕКС: "
			for c := 0; c < len(oldReflexesIdArr); c++ {
				consol += "ID=" + strconv.Itoa(oldReflexesIdArr[c]) + "; "
			}
			for c := 0; c < len(geneticReflexesIdArr); c++ {
				consol += "ID=" + strconv.Itoa(geneticReflexesIdArr[c]) + "; "
			}
			if len(oldReflexesIdArr)> 0 || len(geneticReflexesIdArr) > 0 {
				lib.WritePultConsol(consol)
			} else { // не прописано никаких реакций
				lib.WritePultConsol("Не определен рефлекс")
				if EvolushnStage == 0 { // только для стадии до рождения !!!
					NoUnconditionRefles = "NOREFLEX" + GetCurrentConditionsStr() // СТРОКА УСЛОВИЙ ДЛЯ РЕФЛЕКСА
				}
			}


/*			// ДЛЯ ПСИХИКИ:
			veryActual, targetArrID, acrArr := GetActualReflexAction()
			// сортировка действий рефлексов по убыванию значимости
			acrArr = sortingForActions(targetArrID, acrArr)
			if len(acrArr) > 6 { // может быть и 64 если
				// просто ограничить 3 предположительными акциями
				acrArr = acrArr[:3]
				// на стадии рефлексов был сигнал NOREFLEX и диалог на пульте
			}
			// передать в психику информацию
			psychic.GetReflexInformation(veryActual, targetArrID, acrArr)

			if EvolushnStage < 2 { // сразу запустить имеющиеся рефлексы
				toRunRefleses()
			} // иначе сначала будут проверены автоматизмы в perception.go*/
		}
	} else { // вообще еще нет такого случая :) т.к. всегда есть нулевая
		// сообщить на Пульт, что при данных условиях нет б.рефлекса.
		// if EvolushnStage==0 { // только для стадии до рождения
		// NoUnconditionRefles = "NOREFLEX" + GetCurrentConditionsStr() // СТРОКА УСЛОВИЙ ДЛЯ РЕФЛЕКСА
		// }
		// ничего не делать
	}

	// ДЛЯ ПСИХИКИ: если оставить в верхнем блоке, будут передаваться только безусловные рефлексы
	// так как если есть условные (см стр. 126) - то просто обнулятся безусловные, а самой передачи массива действий условных не будет
	veryActual, targetArrID, acrArr := GetActualReflexAction()
	// сортировка действий рефлексов по убыванию значимости
	acrArr = sortingForActions(targetArrID, acrArr)
	if len(acrArr) > 6 { // может быть и 64 если
		// просто ограничить 3 предположительными акциями
		acrArr = acrArr[:3]
		// на стадии рефлексов был сигнал NOREFLEX и диалог на пульте
	}
	// передать в психику информацию
	psychic.GetReflexInformation(veryActual, targetArrID, acrArr, flgConditionReflexesIdArr)

	if EvolushnStage < 2 { // сразу запустить имеющиеся рефлексы
		toRunRefleses()
	} // иначе сначала будут проверены автоматизмы в perception.go
}

/* очистка актитвировавшихся рефлексов
это надо делать после каждой активации дерева, а не только по факту срабатывания рефлекса
иначе в стадии формирования автоматизмов действия рефлекса будут передаваться в образ действий чисто вербального автоматизма
от предыдущей активации рефлекса, если она была*/
func clinerReflexArrID(){
	oldReflexesIdArr = nil
	geneticReflexesIdArr = nil
	conditionReflexesIdArr = nil
}

// Сортировка действий рефлексов по убыванию актуальной значимости их целей
func sortingForActions(targetArrID[]int, acrArr[]int) []int {
	var impC = make(map[int]int)
	var arr[]int
	var act[]int

	if targetArrID == nil {	return acrArr	}
	for i:=0; i < len(acrArr); i++ {
		act = termineteAction.TerminalActionsTargetsFromID[acrArr[i]]
		if act == nil {
			impC[i + 1000] = acrArr[i] // 1000 рефлекторных действий вряд ли будет
		} else {
			for _, val := range act {
				if lib.ExistsValInArrSort(targetArrID, val) {
					impC[i] = acrArr[i]
				} else {
					impC[i + 1000] = acrArr[i] // 1000 рефлекторных действий вряд ли будет
				}
			}
		}
	}
	vals := make([]int, 0, len(impC))
	for k := range impC {
		vals = append(vals, k)
	}
	sort.Slice(vals , func(i, j int) bool {
		return vals[i] < vals[j]
	})

	for i:=0; i < len(vals); i++ {
		arr = append(arr, impC[vals[i]])
	}
	return arr
}

/* На каждом уровне допускаются ветвления его дочек - для нахождения не точного соотвествия, если не было найдено точное.
Например, если рефлексы имеет только один Базовый контекст, а текущее состояние Beast - сочетание нескольких,
то в результате должны сработать все рефлексы, для каждой цифры в сочетании образа текущих условий.
Но если в рефлексе заданы несколько условий и такой образ точно совпадент с текущим образом условий,
то именно этот рефлекс и сработает.

isExactly: 0 - сработал неточный рефлекс, не смотреть блок точного совпадения
 */
// БЕЗ РЕКУРСИИ т.к. всего 2 уровня проверяется
func findReflexesNodes(level int, cond []int, node *ReflexNode, isExactly int){
	detectedActiveLevel = level
	if len(cond) == 0 { return }

	// если последний уровень Дерева
	if level == 2 { // смотреть условные рефлексы
		if conditionRexlexFound(cond) { return }
	}

	for n := 0; n < len(node.Children); n++ {
		cld := node.Children[n]
		if cld.StyleID == cond[0] {
			// только если нет пусковых стимулов, позволено смотреть древние рефлексы
			if ActiveCurTriggerStimulsID == 0 {
				node,ok:=ReadeReflexTreeFromID(cld.ID)
				if !ok {
					continue
				}
				if node.GeneticReflexID > 0 {
					// древний рефлекс
					oldReflexesIdArr = append(oldReflexesIdArr, node.GeneticReflexID)
					detectedActiveLevel = level + 1
					detectedActiveLastNodID = cld.ID
					return
				}
			}
			// есть ли условный рефлекс?
			if conditionRexlexFound(cond[1:]) { return }
			if cld.ActionID == cond[1] {
				node,ok:=ReadeReflexTreeFromID(cld.ID)
				if !ok {
					continue
				}
				if node.GeneticReflexID > 0{
					geneticReflexesIdArr = append(geneticReflexesIdArr, node.GeneticReflexID)
					detectedActiveLastNodID = cld.ID
					detectedActiveLevel = level + 1
					return
				}
			}
		}
	}
	return
}

func compareCondImade(level int,rArr []int) bool {
	switch level{
	case 1:
		for i := 0; i < len(curBaseCondArr); i++ {
			// есть такое значение в массиве
			for j := 0; j < len(rArr); j++ {
				if curBaseCondArr[i] == rArr[j] {
					return true
				}
			}
		}
	case 2:
		for i := 0; i < len(curPultActionsArr); i++ {
			for j := 0; j < len(rArr); j++ {
				if curPultActionsArr[i] == rArr[j] {
					return true
				}
			}
		}
	}

	return false
}

/* создание иерархии АКТИВНЫХ образов контекстов условий и пусковых стимулов в виде ID образов в [3]int
 создать последовательность уровней условий в виде массива  ID последовательности ID уровней
*/
func getActiveConditionsArr(lev1 int, lev2 int, lev3 int) []int {
	arr := make([]int, 3)

	arr[0] = lev1
	arr[1] = lev2
	arr[2] = lev3

	return arr
}

/* СТРОКА УСЛОВИЙ ДЛЯ безусловного РЕФЛЕКСА типа 1|2,5,8|11
Базовое состояние
Активные контексты
Пусковые стимулы
 */
func GetCurrentConditionsStr() string {
	// ID базового состояния (1 уровень)
	var out = strconv.Itoa(gomeostas.CommonBadNormalWell) + "|"

	// ID (2) актуальных контекстов через запятую
	bs := gomeostas.GetCurContextActiveIDarr()
	for i := 0; i < len(bs); i++ {
		if i > 0 { out += "," }
		out += strconv.Itoa(bs[i])
	}
	out += "|"

	// ID (3) пусковых стимулов через запятую
	as := action_sensor.CheckCurActionsContext()
	for i := 0; i < len(as); i++ {
		if i > 0 { out += "," }
		out += strconv.Itoa(as[i])
	}

	return out
}

// Индикатор реакции игнорирования
func checkIgnorOnly(oldReflexesIdArr []int, geneticReflexesIdArr []int) bool {
	var isIgnor = false

	if len(GeneticReflexes) > 0 && oldReflexesIdArr!=nil {
		gr,ok:=GeneticReflexes[oldReflexesIdArr[0]]
		if ok && len(oldReflexesIdArr) == 1 {
			if gr.ActionIDarr[0] == 9 {
				isIgnor = true
			}
		} else {
			if len(geneticReflexesIdArr) == 1 && GeneticReflexes[geneticReflexesIdArr[0]] != nil {
				gr := GeneticReflexes[geneticReflexesIdArr[0]]
				if gr.ActionIDarr[0] == 9 {
					isIgnor = true
				}
			}
		}
	}

	return isIgnor
}

/* сразу после активации дерева передать инфу для Психики
acrArr:=GetActualReflexAction()
psychic.GetReflexInformation(acrArr)
*/
// вернуть 1)veryActual 2)targetArrID 3)actArtr
func GetActualReflexAction()(bool, []int, []int) {
	var actArtr []int

	/* выявить ID параметров гомеостаза как цели для улучшения в данных условиях
	здесь, чтобы сразу получить veryActual и targetArrID для возврата
	targetArrID - отсортирован по убыванию значимости
	 */
	veryActual, targetArrID := gomeostas.FindTargetGomeostazID()

	// есть ли подходящий по условиям безусловный или условный рефлекс и сделать автоматизм по его действиям
	/* возвращает текущие массивы найденных при активации видов рефлексов
	   1-conditionReflexesIdArr []int - Условные рефлексы
	   2-geneticReflexesIdArr []int - Новые безусловные
	   3-oldReflexesIdArr []int - Древние безусловные
	    condArr,geneticArr,OldArr:=GetActualReflex()
	*/
	condArr, geneticArr, OldArr := GetActualReflex()
	if condArr != nil && len(condArr) > 0 {
		for i := 0; i < len(condArr); i++ {
			//act := ConditionReflexes[condArr[i]]
			act,ok:=ReadeConditionReflexes(condArr[i])
			if !ok {
				continue
			}
			for j := 0; j < len(act.ActionIDarr); j++ {
				actArtr = append(actArtr, act.ActionIDarr[j])
			}
		}
		return veryActual, targetArrID, actArtr
	}
	if geneticArr != nil && len(geneticArr) > 0 {
		for i := 0; i < len(geneticArr); i++ {
			act := GeneticReflexes[geneticArr[i]]
			for j := 0; j < len(act.ActionIDarr); j++ {
				actArtr = append(actArtr, act.ActionIDarr[j])
			}
		}
		return veryActual, targetArrID, actArtr
	}
	if OldArr != nil && len(OldArr) > 0 {
		for i := 0; i < len(OldArr); i++ {
			act := GeneticReflexes[OldArr[i]]
			for j := 0; j < len(act.ActionIDarr); j++ {
				actArtr = append(actArtr, act.ActionIDarr[j])
			}
		}
		return veryActual, targetArrID, actArtr
	}
	// остались самые древние реации - действия по их цели (Какие ID гомео-параметров
	// улучшает действие из http://go/pages/terminal_actions.php)
	// выдать массив возможных действий для данных условий чтобы выбрать одно из них, пока еще не испытанное
	for aID, gIDarr := range termineteAction.TerminalActionsTargetsFromID {
		// выбрать подходящие ID параметров гомеостаза для данной цели
		aArr := lib.GetExistsIntArs(targetArrID, gIDarr)
		if aArr == nil {
			continue
		}
		actArtr = append(actArtr, aID)
	}

	return veryActual, targetArrID, actArtr
}

/* GetActualReflex() возвращает текущие массивы найденных при активации видов рефлексов
1-conditionReflexesIdArr []int - Условные рефлексы
2-geneticReflexesIdArr []int - Новые безусловные
3-oldReflexesIdArr []int - Древние безусловные
 condArr,geneticArr,OldArr:=reflexes.GetActualReflex()
*/
func GetActualReflex()([]int, []int, []int) {
	return conditionReflexesIdArr, geneticReflexesIdArr, oldReflexesIdArr
}
///////////////////////////////////////////////////////////


/////////////////////////////////////////////////
//сторожевой центр сна
func quardingSleepCenter() {
	if !sleep.IsSleeping {
return
	}
/*
	if gomeostas.CommonBadNormalWell == 1{
		sleep.QuardingSleepCenter()// проснуться
	}
 */

	// массив текущих пусковых стимулов
	curPultActionsArr = action_sensor.CheckCurActions()
	if lib.ExistsValInArr(curPultActionsArr, 3) || // наказать
		lib.ExistsValInArr(curPultActionsArr, 10) || // сделать больно
		lib.ExistsValInArr(curPultActionsArr, 15) { // испугаться
		sleep.QuardingSleepCenter()// проснуться
	}
}
///////////////////////////////////////////////////////
____________________________________________

BOT/brain/reflexes/tree_for_pult.go
____________________________________________
/* пока дерева рефлексов на Пульте */

package reflexes

import (
	actionSensor "BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	termineteAction "BOT/brain/terminete_action"
	wordsSensor "BOT/brain/words_sensor"
	"sort"
	"strconv"
)

// образ дерева рефлексов для вывода  на Пульт
var reflesTreeModel = ""

func GetReflexesTreeForPult() string {
	// против паники типа "одновременная запись и считывание карты"
	if notAllowScanInReflexesThisTime { return "!!!" }
	reflesTreeModel = ""
	scanReflexesNodes(-1, &ReflexTree)

	return reflesTreeModel
}

func scanReflexesNodes(level int, node *ReflexNode) {
	if node.ID == 69 {
		node.ID = 69
	}
	if node.ID > 0 {
		reflesTreeModel += setShift(level)
		switch level {
		case 0:
			reflesTreeModel += getStrFromCond(1, node.baseID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 1:
			reflesTreeModel += getStrFromCond(2, node.StyleID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		case 2:
			if node.ActionID == 0 { // древний рефлекс - без пусковыого стимула
				reflesTreeModel += getStrFromCond(2, node.StyleID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
			} else {
				reflesTreeModel += getStrFromCond(3, node.ActionID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
			}
		case 3:
			reflesTreeModel += getStrFromCond(3, node.ActionID) + "(nodeID=" + strconv.Itoa(node.ID) + ")"
		}

		// если есть рефлекс - показать действия
		if node.ConditionedReflex > 0 {
			//rID:=ConditioneReflexes[node.ConditionedReflex]
		} else if node.GeneticReflexID > 0 {
			reflex := GeneticReflexes[node.GeneticReflexID]
			if reflex.ID == 45 {
				reflex.ID = 45
			}
			reflesTreeModel += " <span style='color:blue'>Рефлекс(" + strconv.Itoa(reflex.ID) + "): "+getReflexActionsStrFromID(reflex) + "</span>"
		}
		reflesTreeModel += "<br>\n"
	}
	level++
	for n := 0; n < len(node.Children); n++ {
		scanReflexesNodes(level,&node.Children[n])
	}
}

// отступ
func setShift(level int) string {
	var sh = ""
	for n := 0; n < level; n++ {
		sh += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	}
	return sh
}

// из ID образа получить составляющие в виде строк
func getStrFromCond(level int,imgID int) string {
	var out = ""

	switch level {
	case 1: if imgID > 0 && imgID < 4 { out=gomeostas.GetBaseCondFromID(imgID)
	} else {
		out += "<span style='color:red'>несуществующее Базовое состояние ID=" + strconv.Itoa(imgID) + "</span>"
	}
	case 2:	out = getStrnameFromBaseImageID(imgID)
	case 3: out = getStrnameFromStyleImageID(imgID)
	}

	return out
}

// названия базовых контекстов в их сочетании -из ID их образа
func getStrnameFromBaseImageID(id int) string {
	var out = ""

	img := BaseStyleArr[id]
	for i := 0; i < len(img.BSarr); i++ {
		if i > 0 { out += ", " }
		name := gomeostas.GetBaseContextCondFromID(img.BSarr[i]) + ""
		if len(name) == 0 {
			out += "<span style='color:red'>несуществующий Базовый контекст ID=" + strconv.Itoa(img.BSarr[i]) + "</span>"
		} else {
			out += name
		}
	}

	return out
}

// названия Пусковых стимулов в их сочетании -из ID их образа
func getStrnameFromStyleImageID(id int) string {
	var out = ""

	if id == 0 { return "" }
	img := TriggerStimulsArr[id]
	if img == nil { return "" }
	// Действия с Пульта?
	if len(img.RSarr) > 0 {
		for i := 0; i < len(img.RSarr); i++ {
			if i > 0 { out += ", " }
			name := actionSensor.GetActionNameFromID(img.RSarr[i])
			if len(name) == 0 {
				out += "<span style='color:red'>несуществующий стимул ID=" + strconv.Itoa(img.RSarr[i]) + "</span>"
			} else {
				out += name
			}
		}
	}
	// фразы с Пульта?
	if len(img.PhraseID) > 0 {
		out += "Фразы c ID: "
		for i := 0; i < len(img.PhraseID); i++ {
			if i > 0 { out += ", " }
			out += strconv.Itoa(img.PhraseID[i])
		}
	}

	return out
}

func getReflexActionsStrFromID(reflex *GeneticReflex) string {
	if reflex == nil { return "" }
	var out = ""
	for i := 0; i < len(reflex.ActionIDarr); i++ {
		if i > 0 { out += ", " }
		out += termineteAction.TerminalActonsNameFromID[reflex.ActionIDarr[i]]
	}

	return out
}

// выдать таблицу условных рефлексов для http://go/pages/condition_reflexes.php
func GetConditionReflexInfo(limitBasicID int) string {
	var out = ""

	// сколько рефлексов есть
	ureflexCount := len(ConditionReflexes)
	// если больше 1000 то выдавать только по одному из 3-х базовыз состояний, иначе сильно тормозит
	if ureflexCount > 1000 {
		if limitBasicID == 0 {
			limitBasicID = 1 // начинать с Плохо
		}
	}
	// переключатель диапазона вывода
	if limitBasicID > 0 {
		out += "<br>Показывать: "
		out += "<span style='cursor:pointer;"
		if limitBasicID == 1 { out += "background-color:#FFFF9D;font-weight:bold;" }
		out += "' onClick='show_level(1)'>Плохо</span> "

		out += "<span style='cursor:pointer;"
		if limitBasicID == 2 { out += "background-color:#FFFF9D;font-weight:bold;" }
		out += "' onClick='show_level(2)'>Норма</span> "

		out += "<span style='cursor:pointer;"
		if limitBasicID == 3 { out += "background-color:#FFFF9D;font-weight:bold;" }
		out += "' onClick='show_level(3)'>Хорошо</span> "
	}

	out += "<table class='main_table'  cellpadding=0 cellspacing=0 border=1 width='100%' style='font-size:14px;'>" +
		"<tr><th width=70 class='table_header'>ID<br>рефлекса</th>" +
		"<th width=70 class='table_header'>ID (1 уровень)<br><nobr>базового состояния</nobr></th>" +
		"<th width='25%' class='table_header'>ID (2) актуальных контекстов<br>через запятую</th>" +
		"<th width='25%' class='table_header'>ID (3) пусковых стимулов<br>через запятую</th>" +
		"<th width='25%' class='table_header'>ID действий<br>через запятую</th>" +
		"<th width='25%' class='table_header'>Родитель</th>" +
		"<th width='25%' class='table_header'>Возраст</th>" +
		"<th width='30' class='table_header' title='Удалить рефлекс'>Х</th></tr>"

	if len(ConditionReflexes) == 0 {
		return "<table id='main_table' class='main_table'  cellpadding=0 cellspacing=0 border=1 width='100%' style='font-size:14px;'>" +
		"<tr><th width=70 class='table_header'>ID<br>рефлекса</th>" +
		"<th width=70 class='table_header'>ID (1 уровень)<br><nobr>базового состояния</nobr></th>" +
		"<th width='25%' class='table_header'>ID (2) актуальных контекстов<br>через запятую</th>" +
		"<th width='25%' class='table_header'>ID (3) пусковых стимулов<br>через запятую</th>" +
		"<th width='25%' class='table_header'>ID действий<br>через запятую</th>" +
		"<th width='25%' class='table_header'>Родитель</th>" +
		"<th width='25%' class='table_header'>Возраст</th>" +
		"<th width='30' class='table_header' title='Удалить рефлекс'>Х</th></tr>" +
		"<tr class='highlighting' ><td colspan=7 >Еще нет условных рефлексов.</td></tr>"+
		"</table>"
	}

	keys := make([]int, 0, ureflexCount)
	for k,v := range ConditionReflexes {
		if v==nil{continue}
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, k := range keys {
		//v := ConditionReflexes[k]
		v,ok:=ReadeConditionReflexes(k)
		if !ok {
			continue
		}
		if limitBasicID>0 && v.lev1!=limitBasicID {
			continue
		}
		id := strconv.Itoa(k)
		lev1 := gomeostas.GetBaseCondFromID(v.lev1)
		var lev2 = ""
		for i := 0; i < len(v.lev2); i++ {
			if i > 0 { lev2 += ", " }
			lev2 += gomeostas.GetBaseContextCondFromID(v.lev2[i])
		}
		//act := TriggerStimulsArr[v.lev3]
		act,ok:=ReadeTriggerStimulsArr(v.lev3)
		if !ok {continue }
		var lev3 = ""
		if len(act.RSarr) > 0 {
			for i := 0; i < len(act.RSarr); i++ {
				if i > 0 { lev3 += ", " }
				lev3 += actionSensor.GetActionNameFromID(act.RSarr[i])
			}
		}
		if len(act.PhraseID) > 0 {
			if len(lev3) > 0 {
				lev3 += "<br>"
			}
			for i := 0; i < len(act.PhraseID); i++ {
				if i > 0 { lev3 += "; " }
				w := wordsSensor.GetPhraseStringsFromPhraseID(act.PhraseID[i])
				//w=strings.Trim(w,"")
				lev3 += "\"" + w + "\""
			}
		}
		var tact=""
		for i := 0; i < len(v.ActionIDarr); i++ {
			if i > 0 { tact += ", " }
			tact += termineteAction.TerminalActonsNameFromID[v.ActionIDarr[i]]
		}
		var rank = ""
		if v.rank == 0 {
			rank = "Безусловный"
		} else {
			rank = "Условный"
		}

		t := v.birthTime
		y := int(t / (3600 * 24 * 365))
		d := int((t - y * 3600 * 24 * 365) / (3600 * 24))
		//s := t - (y * 3600 * 24 * 365) - (d * 3600 * 24)
		old := strconv.Itoa(y) + "лет " + strconv.Itoa(d) + "дней "


		out += "<tr >"
		out += "<td class='table_cell' >"+id+"</td>"
		out += "<td class='table_cell' >"+lev1+"</td>"
		out += "<td class='table_cell' >"+lev2+"</td>"
		out += "<td class='table_cell' >"+lev3+"</td>"
		out += "<td class='table_cell' >"+tact+"</td>"
		out += "<td class='table_cell' >"+rank+"</td>"
		out += "<td class='table_cell' >"+old+"</td>"

		out += "<td class='table_cell' align='center' title='Удалить рефлекс' style='cursor:pointer;' ><input type='checkbox' class='deleteCHBX' id='dchbx_" + id + "'></td>"
		out += "</tr>"
	}
	out += "</table>"
	return out
}
____________________________________________

BOT/brain/saver.go
____________________________________________
/*  Функция сохранения всех текущих данных, записываемых в файлы
*/

package brain

import (
	"BOT/brain/psychic"
	"BOT/brain/reflexes"
	termineteAction "BOT/brain/terminete_action"
	"BOT/brain/words_sensor"
)

func init() {
	//res:=SaveAll()
	//if res{	}
}

/* все данные сохраняются при нажатии на Пульте Выключить Beast,
при ручном сохранении файлов (шестеренка)
и в main.go при выходе
!!! только после того, как все данные будут загружены
 */
func SaveAll() bool {
	if PulsCount < 5 { return true }
	var success = true
	defer func() { // ловим панику
		if err := recover(); err != nil {
			success=false
		}
	}()
	// сохранения всего
	// pppp()
	saveLifeTime() 
	word_sensor.SaveWordTree()
	word_sensor.SavePhraseTree()
	termineteAction.SaveTerminalActons()
	// сохранение всех файлов по рефлексам
	reflexes.SaveReflexesAttributes()
	psychic.SaveAllPsihicMemory()
	return success
}

func pppp() { // паника для тектирования
	var n = 1
	p := 12 / (n - 1)
	if p > 0{	}
}
____________________________________________

BOT/brain/sleep/guarding_sleep_center.go
____________________________________________
/*
Сторожевой центр сна
Заставляет проснуться при важных, опасных стимулах c дерева рефлексов.

 */



package sleep

import (
	"BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	"BOT/lib"
)

/////////////////////////////////////////////////////

/* сторожевой центр сна - в reflexes func activeReflexTree(

sleep.QuardingSleepCenter()
 */
func QuardingSleepCenter(){
	if !IsSleeping{
		return
	}

// просыпание
	if sleepNecessityValue < 60 {
		WakeUpping()
	}
}
///////////////////////////////////////////////////////



/* можно ли заснуть и спать - вызывается из sleep.SleepPuls()
Если хочется спать
*/
func IsPossibleToSleep()bool{

	if sleepNecessityValue > 120 {// если слишком большая потребность спать, то заснуть
		return true// заснуть
	}
		if gomeostas.CommonBadNormalWell == 1 {// плохо
			return false // нельзя
		}

		// массив текущих пусковых стимулов
		curPultActionsArr := action_sensor.CheckCurActions()
		if lib.ExistsValInArr(curPultActionsArr, 1) || // непонятно
			lib.ExistsValInArr(curPultActionsArr, 3) || // наказать
			lib.ExistsValInArr(curPultActionsArr, 10) || // сделать больно
			lib.ExistsValInArr(curPultActionsArr, 15) { // испугаться
			return false// нельзя
		}

	return true // да можно заснуть
}
////////////////////////////////////////////
____________________________________________

BOT/brain/sleep/prepare_memory.go
____________________________________________
/*  информационная обработка различных видов памяти

Образ цели бессловестного действия Формируется временно и не сохранятся в файле
Объекты PurposeGeneticObject накапливаются в оперативке и удаляются во сне

*/

package sleep



// обработка накопившегося массива распознанных фраз
func prepareWordArr(){
	/*	wCount:=len(word_sensor.MemoryDetectedArr)
		if wCount==0{

		}*/

if EvolushnStage>3 {
	// обработка эпизодической памяти

	//бъекты PurposeGeneticObject накапливаются в оперативке и удаляются во сне

	// объекты внимания, имеющую высокую значимость importanceObjectID


	// обработка кратковременной памяти во сне или бездействии
//	psychic.ShortTermMemoryProcessing()  есть обработка циклов
}

}
/////////////////////////////////

// все, что нужно во время сна
func prepareAnyArr(){


}
////////////////////////////////////





____________________________________________

BOT/brain/sleep/sleep.go
____________________________________________
/* Cон, его стадии и циклы

_____________________________________________________
В каждом пакете есть флаг  и там для сна выполняется
	if IsSleeping {
		sleepingProcess()
	}
в психике для этого есть psychic_sleep_process.go
_____________________________________________________

начать сон sleep.BeginSleepСondition()

!!!!!!!!
TODO нужно где-то определить функцию засыпания чтобы начать сон sleep.BeginSleepСondition()
и накапливать признаки необходимости сна нужно,
начиная с необходиомсти обработки накопившегося массива распознанных фраз чтобы во сне prepareWordArr()
!!!!!!!!
*/

package sleep

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
)

// ////////////////////////////////////////
var SlipPulsCount = 0 // передача тика Пульса из brine.go
var LifeTime = 0
var EvolushnStage = 0 // стадия развития

var sleepСonditionPulsCount = 0 // начат пульс во время сна

var isStageDreams = false // это стадия сновидений

// коррекция текущего состояния с каждым пульсом
func SleepPuls(evolushnStage int, lifeTime int, puls int) {
	isStageDreams = false
	lib.BlockingAnyActions = false

	LifeTime = lifeTime
	EvolushnStage = evolushnStage
	SlipPulsCount = puls // передача номера тика из более низкоуровневого пакета

	// разбудить при включении. Сторожевая функция - в Пульсе рефлексов вызывает sleep.WakeUpping()
	if SlipPulsCount > 3 {
		WakeUpping() // разбудить при включении
	}

	if SlipPulsCount > 5 {
		sleepNecessityDetector()
	}

	// ход процесса сна
	if IsSleeping {
		sleepСonditionPulsCount++

		// просыпание из-за чего-то - в guarding_sleep_center.go

		prepareWordArr()     // обработка накопившегося массива распознанных фраз
		prepareAnyArr()      //
		isStageDreams = true // начать стадию сновидений - после первичной обработки

		/* последовательность циклов сна по пульсу
		После основной очистки начинается процесс сновидения с оптимизацией эпиз.памяти и заполнением мот.Правил.
		TODO:
		*/
		if isStageDreams {
			lib.BlockingAnyActions = true // блокировать выдачу на пульт любых действий
			//	psychic.GotoDreaming()  ЗДЕСЬ ИСПОЛЬЗОВАТЬ ФУНКЦИИ пассивного режима, а не включать сам режим!

			isStageDreams = false // закончился цикл сновидений в психике, можно прервать

		}
		// можно просыпаться, все, что нужно во сне сделано
		if sleepNecessityValue < 10 {
			WakeUpping()
			return
		}
	}
	////////////////////////////////////////////

	// можно ли заснуть
	if !IsSleeping {
		if sleepNecessityValue > 60 {
			if IsPossibleToSleep() {
				BeginSleepСondition()
			}
		}
	}

	// понижение повреждений во сне каждый пульс или в час 0.01*3600=36
	if !gomeostas.NotAllowSetGomeostazParams {
		gomeostas.GomeostazParams[8] -= 0.01
		if gomeostas.GomeostazParams[8] < 0 {
			gomeostas.GomeostazParams[8] = 0
		}
	}

}

//////////////////////////////////////////////////////

____________________________________________

BOT/brain/sleep/sleep_process.go
____________________________________________
/* процесс погружения в сон и его стадии и циклы

Период сновидений (обработки оставшейся со дня информации) начинается с активации
CurrentInformationEnvironment - текущего образа информационной среды - выявляются актуальные проблемы
и активные доминанты нерешенной проблемы.
Это становится контекстом развития экспериментирования с оставшимися со дня образами.


при повышении активности "Хочу спать"
Механизм преждевременного пробуждения.

В каждом пакете есть флаг  и там для сна выполняется
	if IsSlipping {
		sleepingProcess()
	}
в психике для этого есть psychic_sleep_process.go


Сон начинается с перевода главного цикла мышления в пассивный режим (сновидения)
Если есть инсайт, то просырание и все заново,
если нет инсайта, начинать по очереди фоновые циклы переводить в главные в пассивном режиме.
Это - на уровне не func consciousnessElementary(), а в func dispetchConsciousnessThinking()->func consciousnessThinking
*/

package sleep

import (
	"BOT/brain/psychic"
	"BOT/lib"
)

// true - это СОН  sleep.IsSleeping
var IsSleeping = false
var SleepingType = 0 // 0 - нет сна, 1 - глубокий сон, 2- сновидение !Фаза сновидений реализуется на уровне психики!

func GetSleepCondition() (bool, int) {
	return IsSleeping, SleepingType
}

// ///////////////////////////////////
var sleepNecessityValue = 0 // параметр необходимости сна
/*
	sleepNecessityValue повышается при бодрствовании от разных причин

и уменьашется во сне, когда эти причины восстанавливаются (редуцируется память и т.п.)
При значении sleepNecessityValue >50 и отсуствии значимых раздражителей наступает сон.
При значении > 100 сон наступает несмотря ни на какие раздражители.

Детектор необходимости сна работает по каждому пульсу.
*/
func sleepNecessityDetector() {

	if IsSleeping {
		return
	}

	sleepNecessityValue = 0 // определяется заново с каждым пульсом

	// число параллельных циклов мышления
	cicleCount := psychic.GetCycleCount()
	sleepNecessityValue += getSNweight(cicleCount, 1000, 0.1) //

	// массив распознанных фраз
	//	words:=len(word_sensor.MemoryDetectedArr)
	//	sleepNecessityValue+=getSNweight(words,1000,0.1)// при превышении на 1000 sleepNecessityValue достигнет 100

	// массив прошлых состояний информационного окружения
	infs := psychic.GetInformationEnvironmentObjectsLength()
	sleepNecessityValue += getSNweight(infs, 100000, 0.001) // при превышении на 100000 sleepNecessityValue достигнет 100

	// массив эпизодической памяти
	/* TODO epizM:=len(psychic.EpisodeMemoryObjects)
	sleepNecessityValue+=getSNweight(epizM,100000,0.001)// при превышении на 100000 sleepNecessityValue достигнет 100
	*/
	// масив curImportanceObjectArr объектов (а не адресов)
	//impM:=psychic.GetcurImportanceObjectArrLength()
	//sleepNecessityValue+=getSNweight(impM,100000,0.001)// при превышении на 100000 sleepNecessityValue достигнет 100

	if sleepNecessityValue > 100 {
		BeginSleepСondition()
	}
}

/*
	рассчитать добавку для sleepNecessityValue для параметра, вызывающего сон (param),

действие которого начинается со значения параметра = beginLimit
умноженного на коэффициент (k) параметра для необходимости сна.
*/
func getSNweight(param int, beginLimit int, k float64) int {
	balance := param - beginLimit
	if balance <= 0 {
		return 0
	}
	val := k * float64(balance)
	res := lib.RoundToInt(val)
	return res
}

///////////////////////////////////

/*
начать сон sleep.BeginSleepСondition()
*/
func BeginSleepСondition() {
	// можно ли заснуть
	if !IsPossibleToSleep() || !psychic.IsPossibleToSleep() { // нельзя спать
		return
	}

	IsSleeping = true
	SleepingType = 2 // сразу же главный цикл мышления перевести в пассивный режим (сновидение)
	psychic.ReadiStatus = 0
	sleepСonditionPulsCount = SlipPulsCount
}

////////////////////////////////

/* разбудить - сторожевая функция по важным объектам
 */
func WakeUpping() {
	if !IsSleeping {
		return
	}
	sleepСonditionPulsCount = 0
	psychic.ReadiStatus = 1
	//	psychic.WakeUpping() // в understanding.go
	IsSleeping = false
}

////////////////////////////////////

____________________________________________

BOT/brain/terminete_action/terminete_action.go
____________________________________________
/*  Выполнение действий, список возможных акций Beast */

package TerminalActions

import (
	"BOT/brain/gomeostas"
	"BOT/lib"
	"fmt"
	"sort"
	"strconv"
	"strings"
)

func init() {
	loadTerminalActons()
}

var EnergyDescrib = []string{
	"нет энергии",
	"Едва (сила=1)",
	"Очень слабо (сила=2)",
	"Слабо (сила=3)",
	"Ощутимо (сила=4)",
	"Средне (сила=5)",
	"Повышенно (сила=6)",
	"Настойчиво (сила=7)",
	"Сильно (сила=8)",
	"Очень сильно (сила=9)",
	"Максимально (сила=10)",
}

/* Генетические цели действий Beast ID гомео-параметров, которые призвано улучшить данное действие - по его ID
Значение 10 означает - для всех параметров - улучшение.

Это – наследственно заданная цель действия, не осознаваемая при его совершении.
Но с опытом каждому действию в конкретных условиях (и к ним добавляются слова и фразы)
будет ассоциироваться смысл (осознаваемая значимость).
*/
var TerminalActionsTargetsFromID = make(map[int][]int)

// ПУЛЬС
var ReflexPulsCount = 0 // передача тика Пульса из brine.go
var LifeTime = 0
var EvolushnStage = 0 // стадия развития
var IsSlipping = false

// коррекция текущего состояния гомеостаза и базового контекста с каждым пульсом
func TermineteActionCountPuls(evolushnStage int, lifeTime int, puls int, isSlipping bool) {
	LifeTime = lifeTime
	EvolushnStage = evolushnStage
	ReflexPulsCount = puls // передача номера тика из более низкоуровневого пакета
	IsSlipping = isSlipping

	pulsSimpleReflexex()
}

/* затратные последствия ответного действия
т.е. то, как изменятся параметры гомеостаза при совершении данного действия
*/
type TerminalActionsExpenses struct {
	GomeoID int     // ID затрагиваемого параметра гоместаза
	Diff    float64 // на сколько изменится параметр
}

// имя ответного действия по его ID
var TerminalActonsNameFromID = make(map[int]string)

// затратные последствия ответного действия по его ID
var TerminalActionsExpensesFromID = make(map[int][]TerminalActionsExpenses)
var LastTerminalActons = 0 // максимальный номер действия

// Загрузить акции
func loadTerminalActons() {
	TerminalActonsNameFromID = make(map[int]string)
	lines, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/terminal_actons.txt")
	for i := 0; i < len(lines); i++ {
		p := strings.Split(lines[i], "|")
		id, _ := strconv.Atoi(p[0])
		if LastTerminalActons < id {
			LastTerminalActons = id
		}
		TerminalActonsNameFromID[id] = p[1]
		//это действия приводит к изменению гомео-параметров:
		var expenses []TerminalActionsExpenses
		se := strings.Split(p[2], ";")
		for j := 0; j < len(se); j++ {
			if len(se[j]) == 0 {
				continue
			}
			e := strings.Split(se[j], ">")
			e1, _ := strconv.Atoi(e[0])
			e2, _ := strconv.ParseFloat(e[1], 64)
			expenses = append(expenses, TerminalActionsExpenses{e1, e2})
		}
		TerminalActionsExpensesFromID[id] = expenses

		tg := strings.Split(p[3], ",")

		var tgArr []int
		for j := 0; j < len(tg); j++ {
			if len(tg[j]) == 0 {
				continue
			}
			gID, _ := strconv.Atoi(tg[j])
			tgArr = append(tgArr, gID)
		}
		TerminalActionsTargetsFromID[id] = tgArr
	}
	return
}

/* Сохранить массив действий в файл - для модуля обновления update_genom.go */
func SaveTerminalActons() {
	var out = ""

	// сохранение только в режиме личинки Larva
	if gomeostas.EvolushnStage > 0 {
		return
	}

	keys := make([]int, 0, len(TerminalActonsNameFromID))
	for k := range TerminalActonsNameFromID {
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, id := range keys {
		out += strconv.Itoa(id) + "|" + TerminalActonsNameFromID[id] + "|" +
			GetListTerminalActionsExpenses(id) + "|" +
			strings.Join(lib.StrArrToIntArr(TerminalActionsTargetsFromID[id]), ",") + "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/terminal_actons.txt", out)
}

/* Получить строку результатов ответных действий */
func GetListTerminalActionsExpenses(actID int) string {
	var out = ""

	se := TerminalActionsExpensesFromID[actID]
	if se != nil {
		for i := 0; i < len(se); i++ {
			out += strconv.Itoa(se[i].GomeoID) + ">" + fmt.Sprintf("%.1f", se[i].Diff) + ";"
		}
	}
	out = strings.TrimSuffix(out, ";")

	return out
}

/* Обновить список БП, которые улучшаются при действии */
func UpdateActionsTargetsFromID(id int, txt string) {
	var tArr []int

	sArr := strings.Split(txt, ",")
	for i := 0; i < len(sArr); i++ {
		if len(sArr[i]) == 0 {
			continue
		}
		gID, _ := strconv.Atoi(sArr[i])
		tArr = append(tArr, gID)
	}
	TerminalActionsTargetsFromID[id] = tArr
}

/* Обновить массив затратных действий */
func UpdateTerminalActionsExpenses(id int, txt string) {
	var expenses []TerminalActionsExpenses

	se := strings.Split(txt, ";")
	for i := 0; i < len(se); i++ {
		if len(se[i]) == 0 {
			continue
		}
		e := strings.Split(se[i], ">")
		e1, _ := strconv.Atoi(e[0])
		e2, _ := strconv.ParseFloat(e[1], 64)
		expenses = append(expenses, TerminalActionsExpenses{e1, e2})
	}
	TerminalActionsExpensesFromID[id] = expenses
}

// уже выполнявшиеся при бодрствовании простейшие рефлексы
var usedSimpleReflexexsID []int

func pulsSimpleReflexex() {
	if IsSlipping && len(usedSimpleReflexexsID) > 0 {
		usedSimpleReflexexsID = nil
	}
}

/* выбрать подходящий простейший рефлекс и вернуть его действие
TerminalActions.ChooseSimpleReflexexAction
Eсли нет условного и безусловного рефлекса, то совершается самый простейший безусловный рефлекс
		по сочетаниям редактора http://go/pages/terminal_actions.php
		Данный редактор связывает действие с тем, какие гомео-параметры улучшает данное действие.
*/
func ChooseSimpleReflexexAction() (bool, int, []int) {
	// выявить ID парамктров гомеостаза как цели для улучшения в данных условиях
	veryActual, targetID := gomeostas.FindTargetGomeostazID()
	// подходящие действия
	var aArr []int
	// выбранные
	var fActsID []int
	// одно из выбранных
	var singleActID = 0
	// выдать массив возможных действий чтобы выбрать одно из них, пока еще не испытанное
	for id, gIDarr := range TerminalActionsTargetsFromID {
		if id > 0 {
			// выбрать подходящие ID параметров гомеостаза для данной цели
			aArr = lib.GetExistsIntArs(targetID, gIDarr)
			if aArr == nil {
				continue
			}
			for i := 0; i < len(aArr); i++ {
				// исключить те, что уже использовались
				if lib.ExistsValInArr(usedSimpleReflexexsID, aArr[i]) {
					continue
				}
				//fActsID = append(fActsID, aArr[i])
				fActsID = append(fActsID, id) // !!! id действия, а не параметр гомеостаза!
			}
		}
	}
	if len(fActsID) == 0 {
		if len(aArr) == 0 {
			return false, 0, nil
		}
		// снова обнулить usedSimpleReflexexsID
		singleActID = aArr[0]
		usedSimpleReflexexsID = nil
	} else {
		singleActID = fActsID[0]
	}
	usedSimpleReflexexsID = append(usedSimpleReflexexsID, singleActID)
	return veryActual, singleActID, targetID
}
//////////////////////////////////////////////////

/* выдать массив возможных действий по ID парамктров гомеостаза как цели для улучшения в данных условиях */
func GetSimpleActionForCurContitions() ([]int, []int) {
	// выявить ID парамктров гомеостаза как цели для улучшения в данных условиях
	_, targetArrID := gomeostas.FindTargetGomeostazID()
	if targetArrID == nil {return nil, nil} // если целей нет, незачем дальше проверять - будет nil

	// выявить ID параметров гомеостаза как цели для улучшения в данных условиях
	var fActsID []int
	// выдать массив возможных действий чтобы выбрать одно из них, пока еще не испытанное
	for id, gIDarr := range TerminalActionsTargetsFromID {
		if id > 0 {
			// выбрать подходящие ID параметров гомеостаза для данной цели
			aArr := lib.GetExistsIntArs(targetArrID, gIDarr)
			if aArr == nil {
				continue
			}
			for i := 0; i < len(aArr); i++ {
				fActsID = append(fActsID, id) // !!! id действия, а не параметр гомеостаза!
			}
		}
	}

	return targetArrID, fActsID
}
____________________________________________

BOT/brain/transfer/transfer.go
____________________________________________
/*  для предотвращения цикличности включений, при такой необходимости
т.е. можно использовать этот пакет как промежуточный для передачи данных или вызовов функций.
*/

package transfer

/*
	Переключатель игрового режима

Игровой режим включается по нажатию кнопки Поиграть.
Выключается по окончании времени ожидания 120 сек после последней плашки ожидания
или по нажатию на крестик индикатора игрового режима.
*/
var IsPsychicGameMode = false

/*
	Переключатель учительского режима

включается по нажатию кнопки Поучить.
Выключается по окончании времени ожидания 120 сек после последней плашки ожидания
или по нажатию на крестик индикатора режима.
*/
var IsPsychicТeachingMode = false

____________________________________________

BOT/brain/update/update_genom.go
____________________________________________
/* Обмен базовыми данными происходит по схеме:
1. Заводится общий каталог, в который выводятся данные ботов в виде структурированных файлов,
которыми они хотят обменяться. Имя файла в формате: имя бота_имя файла. Например:
bot1_update_phrase_tree.txt.
2. В каталоге memory_save создается файл каталога обмена update_dir.txt, в котором указывается список файлов для
обновления:
1|bot2|update_phrase_tree|2022-07-12 09:40:48|200|1
1 - номер очередности загрузки. ВАЖНО: сначала файл действий, потом рефлексов.
bot2 - имя внешнего бота
update_phrase_tree - имя файла обмена (см. константы ниже)
2022-07-12 09:40:48 - дата/время, заполняется автоматом после успешного обновления
200 - ID последней записи при экспорте.
1 - статус блокировки записи: 0 - обмен заблокирован, 1 - обмен разрешен
3. Каждый бот следит за своим файлом, обновляя их. Чужие только читает. При обновлени делается проверка на
совместимость БП и БК, список действий.
Прочий обмен это обмен первичными сенсорами: чтобы бот правильно расположил их по дереву надо ему просто скормить
дерево фраз другого бота.
При экпорте фиксируется ID последней записи массива, и при следующем экспорте будут выводиться записи начианая
с ID + 1.
*/
package update

import (
	"BOT/brain/reflexes"
	termineteAction "BOT/brain/terminete_action"
	"BOT/brain/words_sensor"
	"BOT/lib"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
)

/* Шаблон дата-время
Может возникнуть справедливый вопрос: а что же такого магического есть в Mon Jan 2 15:04:05 MST 2006?
Давайте посмотрим на шаблон в другом порядке:
01/02 03:04:05PM 06-0700
Видно, что здесь нет двух одинаковых полей. А это означает, что для такой конкретной даты каждое поле будет
точно идентифицированным вне зависимости от форматирования. */
var Layout = "2006-01-02 15:04:05"

// Имя бота
var botName string

// каталог обмена
var pathUpdate string

// имя файла обновления фраз
const updatePhraseName = "update_phrase_tree"

// имя файла обновления рефлекторных действий
const updateActonsName = "update_terminal_actons"

// имя файла обновления рефлексов
const updateDnkReflexes = "update_dnk_reflexes"

// имя файла обновления пусковых стимулов У-рефлекса
const updateTriggerStimulsImages = "update_trigger_stimuls_images"

// имя файла обновления У-рефлексов
const updateConditionReflexes = "update_condition_reflexes"

func init() {
	runFile:=lib.GetMainPathExeFile()
	address := lib.ReadFileContent(runFile + "/common/linking_address.txt")
	botName = strings.TrimSpace(address)[7:]
	botName = strings.Split(botName, ":")[0]
	pathUpdate = strings.Replace(runFile, "\\"+botName, "\\update", 1)
	LoadFileUpdate()
}

// структура каталога файлов обмена
type FileMod struct {
	ID       int       // ID записи
	FileName string    // имя файла
	LastMod  time.Time // дата-время последнего изменения
	LastID   int       // ID последней импортированной записи
	Compat   int       // совместимость файлов обменна: 0 - не совместмый, 1 - совместимый
}

var FileUpdate = make(map[string]string)   // каталог обмена
var FileUpdateDir = make(map[int]*FileMod) // подкаталог обмена

/* Загрузить каталог обмена */
func LoadFileUpdate() {
	var sArr []string
	var tArr []string
	var i, id, MaxNum, Compat, LastID int
	var FileName, botNameBuf, idList string
	var LastMod time.Time

	sArr, _ = lib.ReadLines(lib.GetMainPathExeFile() + "/memory_save/update_dir.txt")
	MaxNum = len(sArr)
	if MaxNum == 0 {
		return
	}
	FileUpdateDir = make(map[int]*FileMod, MaxNum)

	for i = 0; i < MaxNum; i++ {
		tArr = strings.Split(sArr[i], "|")
		if botNameBuf != tArr[1] {
			if botNameBuf != "" {
				FileUpdate[botNameBuf] = strings.TrimSuffix(idList, "|")
			}
			botNameBuf = tArr[1]
			idList = ""
		}
		idList += tArr[0] + "|"
		if i == MaxNum-1 {
			idList = strings.TrimSuffix(idList, "|")
		}
		FileUpdate[botNameBuf] = idList
		FileName = tArr[2]
		id, _ = strconv.Atoi(tArr[0])
		LastMod, _ = time.Parse(Layout, tArr[3]) // строка в дату/время
		LastID, _ = strconv.Atoi(tArr[4])
		Compat, _ = strconv.Atoi(tArr[5])
		EditAddFileUpdate(id, FileName, LastMod, LastID, Compat)
	}
}

/* Обновить каталог обмена */
func EditAddFileUpdate(ID int, FileName string, LastMod time.Time, LastID int, Compat int) *FileMod {
	var node FileMod

	node.ID = ID
	node.FileName = FileName
	node.LastMod = LastMod
	node.LastID = LastID
	node.Compat = Compat
	FileUpdateDir[ID] = &node

	return &node
}

// сохранить изменения в файле обмена
func SaveFileUpdateDir() {
	var sArr []string
	var i, id int
	var out, fileName, LastMod, LastID, compat string

	for BotName, idList := range FileUpdate {
		sArr = strings.Split(idList, "|")
		for i = 0; i < len(sArr); i++ {
			id, _ = strconv.Atoi(sArr[i])
			fileName = FileUpdateDir[id].FileName
			LastMod = FileUpdateDir[id].LastMod.Format(Layout)
			LastID = strconv.Itoa(FileUpdateDir[id].LastID)
			compat = strconv.Itoa(FileUpdateDir[id].Compat)
			out += strconv.Itoa(id) + "|" + BotName + "|" + fileName + "|" + LastMod + "|" + LastID + "|" + compat + "\r\n"
		}
	}
	out = strings.TrimSuffix(out, "\r\n")
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_save/update_dir.txt", out)
}

/* Импорт из файла обмена
Загружаем поочередно все типы файлов от ботов, которые прописаны в каталоге
*/
func ImportFileUpdate(flieArr []int) (bool, string) {
	var sArr []string
	var tArr []string
	var IsMod, IsNoCompat, IsNoAllImport, IsCompat, FlgBreak bool
	var i, j, id, compat, aktIdCur int
	var FileName, updateName, FilePathUpdate, msgTxt, outTxt, nameAkt string

	// смотрим каталог обмена и закачиваем согласно списку ботов их файлы
	for BotName, ListId := range FileUpdate {
		sArr = strings.Split(ListId, "|")
		for i = 0; i < len(sArr); i++ {
			id, _ = strconv.Atoi(sArr[i])
			if !lib.ExistsValInArr(flieArr, id) {
				continue // обрабатываем только заданные файлы
			}
			compat = FileUpdateDir[id].Compat
			if compat == 0 {
				continue // не совместимый для обмена файл
			}
			IsCompat = true // хотя бы один файл в списке обновлений не блокирован по совместимости
			FileName = FileUpdateDir[id].FileName
			FilePathUpdate = pathUpdate + "/" + BotName + "_" + FileName + ".txt"
			// смотрим дату последнего изменения файла обмена
			file, err := os.Stat(FilePathUpdate)
			if err != nil || file.Size() == 0 {
				outTxt += "Файл не найден или пустой: " + BotName + "_" + FileName + "</br>"
				IsNoAllImport = true
				continue
			}
			// сравниваем дату/время изменения файла с зафиксированной при последнем обновлении
			ModTime := file.ModTime() // считывает с учетом часового пояса!
			// убираем пояс из времени
			ModtimeTxt := ModTime.Format(Layout)
			ModtimeLayout, _ := time.Parse(Layout, ModtimeTxt)
			LastMod := FileUpdateDir[id].LastMod
			dur := LastMod.Sub(ModtimeLayout) // сравнивает с учетом пояса, поэтому его надо убрать заранее!
			if dur != 0 {
				tArr, _ = lib.ReadLines(FilePathUpdate)
				if len(tArr) == 0 {
					continue
				}
				if FileName == updateActonsName {
					ActonsSincID = make(map[int]int)
				}
				if FileName == updateTriggerStimulsImages {
					TriggerSincID = make(map[int]int)
				}
				for j = 0; j < len(tArr); j++ {
					if tArr[j] != "" {
						switch FileName {
						case updatePhraseName: // дерево фраз
							updateName = updatePhraseName
							// включаем флаг авторитарной записи новой фразы, без выдержки в tempArr
							word_sensor.NoCheckWordCount = true
							word_sensor.SetNewPhraseTreeNode(tArr[j])
							word_sensor.NoCheckWordCount = false
						case updateActonsName: // список действий
							updateName = updateActonsName
							// делаем проверки на совместимость БП и БК, закачиваем только новые действия
							// уникальный идентификатор действия: имя + комбинация воздействий в любом порядке
							// при этом списка воздействия может не быть
							akt := strings.Split(tArr[j], "|")
							aktId, _ := strconv.Atoi(akt[0])
							if akt[1] == "" {
								continue // действие без имени игнорируется
							} else {
								if akt[2] == "" {
									ActonsSincID[aktId] = aktId
									nameAkt = akt[1]
								} else {
									aktIdCur, nameAkt = IsNameActionsToBot(akt[1], akt[2])
									// фиксируем связь ID текущего и внешнего ботов
									ActonsSincID[aktId] = aktIdCur
									if aktIdCur == -1 { // не совместимое действие по БП или БК
										continue
									}
								}
							}
							// есть такое действие по ключу: совпадают имя и действие (которое может быть пустым)
							if aktIdCur > 0 {
								continue
							}
							if akt[3] != "" {
								if IsListActionToBot(akt[3], 8) == false {
									continue
								}
							}
							// если попалась хотя бы одна НОВАЯ запись в режиме 0 - то отбой
							// массив соответствий будет пустой, импорт бу/у рефлексов и пусковых стимулов так же откатится
							if reflexes.EvolushnStage > 0 {
								msgTxt = "Обнаружено новое Действие. Импорт новых Действий возможен только в начальной стадии 0: "
								FlgBreak = true
								break
							}
							// если дошло до этого места, значит все проверки прошли и такого действия нет в базе текущего бота
							// и его можно залить, зафиксировав при этом связь между ID действий ботов
							termineteAction.LastTerminalActons++
							aktIdNew := termineteAction.LastTerminalActons
							ActonsSincID[aktId] = aktIdNew // фиксируем связь ID текущего и внешнего ботов
							termineteAction.TerminalActonsNameFromID[aktIdNew] = nameAkt
							termineteAction.UpdateTerminalActionsExpenses(aktIdNew, akt[2])
							termineteAction.UpdateActionsTargetsFromID(aktIdNew, akt[3])
						case updateDnkReflexes: // список рефлексов
							updateName = updateDnkReflexes
							if len(ActonsSincID) == 0 { // если массив соответствий ID действий пустой, нет смысла проверять
								if reflexes.EvolushnStage > 0 {
									msgTxt = "Импорт новых БУ-рефлексов возможен только в начальной стадии 0: "
								} else {
									msgTxt = "Массив соотвествий ID действий пустой. БУ-рефлексы импортируются в одном пакете в очередности: действия, рефлексы: "
								}
								FlgBreak = true
								break
							}
							// делаем проверки на совместимость
							rf := strings.Split(tArr[j], "|")
							// совместимость базовых состояний
							lev1, _ := strconv.Atoi(rf[1])
							if lib.ExistsValInArr([]int{1, 2, 3}, lev1) == false {
								continue
							}
							// совместимость базовых контекстов
							if IsCompareArrValue(rf[2], 12) == false {
								continue
							}
							// совместимость пусковых стимулов
							if IsCompareArrValue(rf[3], 17) == false {
								continue
							}
							// совместимость рефлекторных действий
							rf[4] = IsActionIdToBot(rf[4])
							if rf[4] == "-1" {
								continue
							} // проверка наличия рефлекса в базе делается в CreateNewGeneticReflex(), где создаются только новые
							lev2 := lib.IntArrToStrArr(strings.Split(rf[2], ","))
							lev3 := lib.IntArrToStrArr(strings.Split(rf[3], ","))
							aktArr := strings.Split(rf[4], ",")
							reflexes.CreateNewGeneticReflex(0, lev1, lev2, lev3, lib.IntArrToStrArr(aktArr),true)
						case updateTriggerStimulsImages: // список пусковых стимулов У-рефлексов
							updateName = updateTriggerStimulsImages
							if len(ActonsSincID) == 0 { // если массив соответствий ID действий пустой, нет смысла проверять
								if reflexes.EvolushnStage != 1 {
									msgTxt = "Импорт новых пусковых стимулов У-рефлексов возможен только в стадии 1: "
								} else {
									msgTxt = "Массив соотвествий ID действий пустой. Пусковые стимулы У-рефлексов импортируются в одном пакете в очередности: действия, стимулы: "
								}
								FlgBreak = true
								break
							}
							rt := strings.Split(tArr[j], "|")
							trId, _ := strconv.Atoi(rt[0])
							rt[1] = IsActionIdToBot(rt[1])
							if rt[1] == "-1" {
								continue
							} // не нашлось соответствий в массиве
							ton := 0
							if rt[3] != "" {
								ton, _ = strconv.Atoi(rt[3])
								if lib.ExistsValInArr([]int{0, 3, 4}, ton) == false {
									continue // тон не совпадает
								}
							}
							mod := 0
							if rt[4] != "" {
								mod, _ = strconv.Atoi(rt[4])
								if lib.ExistsValInArr([]int{0, 20, 21, 22, 23, 24, 25, 26}, mod) == false {
									continue // настроение не совпадает
								}
							}
							if rt[2] != "" { // под вопросом: надо ли позволять создавать триггер без кода фразы
								word_sensor.NoCheckWordCount = true
								phr := strings.Split(rt[2], "#")
								for p := 0; p < len(phr); p++ {
									word_sensor.SetNewPhraseTreeNode(phr[p])
								}
								word_sensor.NoCheckWordCount = false
								rsr := strings.Split(rt[1], ",")
								rsar := lib.IntArrToStrArr(rsr)
								// проверка наличия пускового стимула в базе делается в CreateNewlastTriggerStimulsID(), где создаются только новые
								TriggerSincID[trId], _ = reflexes.CreateNewlastTriggerStimulsID(0, rsar, word_sensor.CurrentPhrasesIDarr, ton, mod,true)
							}
						case updateConditionReflexes: // список У-рефлексов
							updateName = updateConditionReflexes
							if len(ActonsSincID) == 0 { // если массив соответствий пустой, нет смысла проверять
								if reflexes.EvolushnStage != 1 {
									msgTxt = "Импорт новых У-рефлексов возможен только в стадии 1: "
								} else {
									msgTxt = "Массив соотвествий ID действий пустой. У-рефлексы импортируются в одном пакете в очередности: действия, бу-рефлексы, стимулы, у-рефлексы: "
								}
								FlgBreak = true
								break
							}
							if len(TriggerSincID) == 0 { // если массив соответствий пустой, нет смысла проверять
								msgTxt = "Массив соотвествий ID пусковых стимулов пустой. У-рефлексы импортируются в одном пакете в очередности: действия, бу-рефлексы, стимулы, у-рефлексы"
								FlgBreak = true
								break
							}
							// делаем проверки на совместимость
							rcf := strings.Split(tArr[j], "|")
							rfuId, _ := strconv.Atoi(rcf[0])
							// совместимость базовых состояний
							lev1, _ := strconv.Atoi(rcf[1])
							if lib.ExistsValInArr([]int{1, 2, 3}, lev1) == false {
								continue
							}
							// совместимость базовых контекстов
							if IsCompareArrValue(rcf[2], 12) == false {
								continue
							}
							lev2 := lib.IntArrToStrArr(strings.Split(rcf[2], ","))
							// совместимость пусковых триггеров
							lev3 := 0
							if _, ok := TriggerSincID[rfuId]; ok {
								lev3 = TriggerSincID[rfuId]
							} else {
								continue
							}
							// совместимость рефлекторных действий
							rcf[4] = IsActionIdToBot(rcf[4])
							if rcf[4] == "-1" {
								continue
							} // не нашлось соответствий в массиве
							aktArr := strings.Split(rcf[4], ",")
							reflexes.CreateNewConditionReflex(0, lev1, lev2, lev3, lib.IntArrToStrArr(aktArr), 0,true)
						}
						if FlgBreak == true {
							break
						} else {
							IsMod = true      // прочитана хотя бы одна запись с файла
							IsNoCompat = true // все проверки на совместимость успешны хотя бы для одной записи
						}
					}
				}
				LastModeUpdate(BotName, updateName, ModTime)
				SaveFileUpdateDir()
			}
			// сохраняем данные
			if IsMod == true {
				IsMod = false
				switch updateName {
				case updateActonsName:
					termineteAction.SaveTerminalActons()
				case updateDnkReflexes:
					reflexes.SaveGeneticReflexes()
				case updateTriggerStimulsImages:
					reflexes.SaveTriggerStimulsArr()
				case updateConditionReflexes:
					reflexes.SaveConditionReflex()
				}
				msgTxt = "Успешный импорт: "
			} else {
				// не совместимый файл обмена
				if IsNoCompat == true {
					FileUpdateDir[id].Compat = 0
					IsNoCompat = false
					msgTxt = "Не совместимый для импорта файл обмена: "
				} else {
					msgTxt = "Нет новых данных в файле: "
				}
				IsNoAllImport = true
			}
			// очищаем массивы совметимостей
			if updateName == updateConditionReflexes {
				ActonsSincID = make(map[int]int)
				TriggerSincID = make(map[int]int)
			}
			outTxt += "<div align='left'>" + msgTxt + BotName + "_" + FileName + "</div>"
		}
	}
	// все файлы заблокированы, нечего обновлять
	if IsCompat == false {
		outTxt = "Все файлы в списке обмена не совместимы для импорта!"
		IsNoAllImport = true
	}

	return !IsNoAllImport, outTxt
}

/* Экспорт в файл обмена
Выгружаем типы файлов, указанные через номера строк flieArr[] в каталоге "memory_save/update_dir.txt" по одному разу
Блокировка файлов при экспорте не учитывается потому, что экспортировать свои данные можно в любом случае
*/
func ExportFileUpdate(flieArr []int) (bool, string) {
	var sArr []string
	var out, outBuf, FileName, FileNameList, msgTxt, outTxt string
	var i, id, cnt, LastID, UpdateLastID int
	var flgExp, IsNoAllExp bool

	_, err := os.Stat(pathUpdate)
	if os.IsNotExist(err) {
		lib.WritePultConsol("Каталог обмена не найден: " + pathUpdate)
		return false, ""
	}

	// смотрим каталог обмена и выгружаем согласно списку ботов файлы для них
	for _, ListId := range FileUpdate {
		sArr = strings.Split(ListId, "|")
		for i = 0; i < len(sArr); i++ {
			id, _ = strconv.Atoi(sArr[i])
			if !lib.ExistsValInArr(flieArr, id) {
				continue // обрабатываем только заданные файлы
			}
			FileName = FileUpdateDir[id].FileName
			if lib.ExistsValStrInList(FileNameList, FileName, "|") == true {
				continue // уже выгружали в этом сеансе
			}
			FileNameList += FileName + "|"
			UpdateLastID = FileUpdateDir[id].LastID
			LastID = 0
			switch FileName {
			case updatePhraseName: // дерево фраз
				cnt = len(word_sensor.PhraseTreeFromID)
				if cnt == 0 {break}
				node,ok:=word_sensor.ReadePhraseTreeFromID(cnt-1)
				if ok {
					if node.ID <= UpdateLastID {
						break
					}
					for n := 0; n < cnt; n++ {
						// добавляем самую длинную фразу ветки, конечный узел
//						lastID = word_sensor.PhraseTreeFromID[n].ID
						ws,ok:=word_sensor.ReadePhraseTreeFromID(n)
						if !ok {
							continue
						}
						if ws.ID <= UpdateLastID {
							continue
						}
						// выводим узлы дерева от последнего экспорта
						outBuf = word_sensor.GetPhraseStringsFromPhraseID(LastID)

							if ws.Children == nil {
							if outBuf != "" {
								out += outBuf + "\r\n"
								flgExp = true
							}
						}
						outBuf = ""
					}
				}
			case updateActonsName: // список действий
				// список не большой, просто смотрим число строк, если есть новые - экспортируем
				PathFileExport := lib.MainPathExeFile + "/memory_reflex/terminal_actons.txt"
				lines, _ := lib.ReadLines(PathFileExport)
				cnt = len(lines)
				if cnt == 0 {break}
				if cnt <= UpdateLastID {break}
				flgExp = CopyFileToExport(PathFileExport, FileName)
			case updateDnkReflexes: // список бу-рефлексов
				cnt = len(reflexes.GeneticReflexes)
				if cnt == 0 {break}
				// так как txt список рефлексов отсортирован, просто смотрим последний номер
				if reflexes.GeneticReflexes[cnt-1].ID <= UpdateLastID {break}
				keys := make([]int, 0, len(reflexes.GeneticReflexes))
				for k, v := range reflexes.GeneticReflexes {
					if v.ID <= UpdateLastID {continue}
					keys = append(keys, k)
				}
				sort.Ints(keys)
				for _, k := range keys {
					rf := reflexes.GeneticReflexes[k]
					LastID = rf.ID
					out += reflexes.ListDnkReflex(k) + "\r\n"
					flgExp = true
				}
			case updateTriggerStimulsImages: // список пусковых стимулов У-рефлексов
				if reflexes.EvolushnStage == 0 {
					msgTxt = "экспорт возможен только начиная со стадии 1."
					break
				}
				cnt = len(word_sensor.PhraseTreeFromID)
				if cnt == 0 {break}

				ws,ok:=word_sensor.ReadePhraseTreeFromID(cnt-1)
				if ok {
					if ws.ID <= UpdateLastID {
						break
					}
					out = ""
					for k, v := range reflexes.TriggerStimulsArr {
						if v==nil{continue}
						if k > LastID {
							LastID = k
						}
						if k <= UpdateLastID {
							continue
						}
						out += strconv.Itoa(k) + "|"
						for i := 0; i < len(v.RSarr); i++ {
							out += strconv.Itoa(v.RSarr[i]) + ","
						}
						out += "|"
						for i := 0; i < len(v.PhraseID); i++ {
							tmpArr := word_sensor.GetWordArrFromPhraseID(v.PhraseID[i])
							if tmpArr != nil {
								out += strings.TrimSpace(word_sensor.GetStrFromArrID(tmpArr)) + "#"
							}
						}
						out += "|" + strconv.Itoa(v.ToneID) + "|" + strconv.Itoa(v.MoodID) + "\r\n"
						flgExp = true
					}
				}
			case updateConditionReflexes: // список у-рефлексов
				if reflexes.EvolushnStage == 0 {
					msgTxt = "экспорт возможен только начиная со стадии 1."
					break
				}
				cnt = len(reflexes.ConditionReflexes)
				if cnt == 0 {break}
				node,ok:=reflexes.ReadeConditionReflexes(cnt-1)
				if !ok {
					continue
				}
				rID:=node.ID
				if rID <= UpdateLastID {break}
				for k, v := range reflexes.ConditionReflexes {
					if v==nil{continue}
					if k > LastID {LastID = k}
					if k <= UpdateLastID {continue}
					out += reflexes.ListConditionReflex(k, v) + "\r\n"
					flgExp = true
				}
			}
			if flgExp == true {
				flgExp = false
				outTxt += "<div align='left'>" + botName + "_" + FileName + ": ОК</div>"
				FileUpdateDir[id].LastID = LastID
				if out != "" {
					out = strings.TrimSuffix(out, "\r\n")
					lib.WriteFileContent(pathUpdate + "/" + botName + "_" + FileName + ".txt", out)
					out = ""
				}
			} else {
				if msgTxt == "" {
					if cnt == 0 {
						msgTxt = "файл пустой, нет данных для экспорта."
					} else {
						msgTxt = "нет новых данных для экспорта."
					}
				}
				IsNoAllExp = true
				outTxt += "<div align='left'>" + botName + "_" + FileName + ": " + msgTxt + "</div>"
			}
			SaveFileUpdateDir()
			msgTxt = ""
		}
	}
	return !IsNoAllExp, outTxt
}

/* Копировать файл данных в общий каталог */
func CopyFileToExport(PathFileExport string, updateName string) bool {
	file, err := os.Stat(PathFileExport)
	if err == nil {
		if file.Size() > 0 {
			lib.CopyFile(PathFileExport, pathUpdate+"/"+botName+"_"+updateName+".txt")
			return true
		} else {
			lib.WritePultConsol("Файл пустой: " + botName + "_" + updateName)
		}
	}
	return false
}

/* Обновить дату/время в файле списка обмена */
func LastModeUpdate(BotName string, FileName string, TimeUpdate time.Time) {
	var sArr []string
	var ListID string
	var i, id int

	ListID = FileUpdate[BotName]
	sArr = strings.Split(ListID, "|")
	for i = 0; i < len(sArr); i++ {
		id, _ = strconv.Atoi(sArr[i])
		if FileUpdateDir[id].FileName == FileName {
			// убираем часовой пояс из времени
			ModtimeTxt := TimeUpdate.Format(Layout)
			ModtimeLayout, _ := time.Parse(Layout, ModtimeTxt)
			FileUpdateDir[id].LastMod = ModtimeLayout
			break
		}
	}
}

// проверка совместимости списка значений с заданным рядом значений в массиве
func IsCompareArrValue(txt string, typeArr int) bool {
	var CntArr []int
	var i int

	if txt == "" {
		return true
	} // если список пустой, то нечего проверять

	for i = 1; i < typeArr+1; i++ {
		CntArr = append(CntArr, i)
	}

	sArr := strings.Split(txt, ",")
	for i = 0; i < len(sArr); i++ {
		id, _ := strconv.Atoi(sArr[i])
		if lib.ExistsValInArr(CntArr, id) == true {
			return true
		}
	}

	return false
}

/* Массив соответствия ID рефлекторных действий текущего и внешнего ботов
Заполняется при загрузке файла действий и очищается при завершении загрузки файла У-рефлексов */
var ActonsSincID = make(map[int]int)

/* Массив соответствия ID пускровых стимулов У-рефлекса текущего и внешнего ботов
Заполняется при загрузке файла триггеров и очищается при завершении загрузки файла У-рефлексов */
var TriggerSincID = make(map[int]int)

/* Поиск номера рефлекторного действия в базе текущего бота
соответствующий номеру внешнего бота. Для этого был заполнен массив соответствий
при загрузке файла действий. */
func IsActionIdToBot(txt string) string {
	var out string

	// если база пустая, то прекращаем проверять
	if len(termineteAction.TerminalActonsNameFromID) == 0 {
		return txt
	}
	if txt == "" {
		return ""
	}

	sArr := strings.Split(txt, ",")
	for i := 0; i < len(sArr); i++ {
		id, _ := strconv.Atoi(sArr[i])
		if as, ok := ActonsSincID[id]; ok {
			out += strconv.Itoa(as) + ","
		}
	}
	if out == "" {
		out = "-1"
	} else {
		out = strings.TrimSuffix(out, ",")
	}
	return out
}

/* Проверка наличия рефлекторного действия в базе текущего бота по ключу: имя + действие
Нужно проверять пары БП>Val не как целую строку, а в любом порядке вхождения в строке
*/
func IsNameActionsToBot(ActName string, ActList string) (int, string) {
	var tArr []string
	var id, i, j, n, num int
	var aktLst, NewName string
	var isFind bool
	var err error

	// если база пустая или действия не совместимы то прекращаем проверять
	if len(termineteAction.TerminalActonsNameFromID) == 0 {
		return 0, ActName
	}
	if len(termineteAction.TerminalActionsExpensesFromID) == 0 {
		return 0, ActName
	}

	// проверяем совместимость базовых параметров в строке затрат действия
	sArr := strings.Split(ActList, ";")
	for i = 0; i < len(sArr); i++ {
		aktLst += strings.Split(sArr[i], ">")[0] + ","
	}
	aktLst = strings.TrimSuffix(aktLst, ",")
	if IsListActionToBot(aktLst, 12) == false {
		return -1, ""
	}
	aktLst = ""

	for _, val := range termineteAction.TerminalActonsNameFromID {
		if val == ActName {
			isFind = true
			break
		}
	}
	if isFind == false {
		return 0, ActName
	} else {
		// создаем новое имя
		poz := strings.LastIndex(ActName, "_")
		if poz > 0 {
			if num, err = strconv.Atoi(ActName[poz+1:]); err != nil {
				num = 0
			}
			ActName = ActName[:poz]
		}
		NewName = ActName + "_" + strconv.Itoa(num+1)
	}

	// формируем сводку действий по имени ActName
	for AktId := range termineteAction.TerminalActionsExpensesFromID {
		se := termineteAction.TerminalActionsExpensesFromID[AktId]
		if termineteAction.TerminalActonsNameFromID[AktId] == ActName {
			for i = 0; i < len(se); i++ {
				aktLst += strconv.Itoa(se[i].GomeoID) + ">" + fmt.Sprintf("%.1f", se[i].Diff) + ";"
			}
			aktLst = strings.TrimSuffix(aktLst, ";")
			tArr = append(tArr, strconv.Itoa(AktId)+"|"+aktLst)
			aktLst = ""
		}
	}

	// ищем совпадения в любой последовательности с ActList
	sArr = strings.Split(ActList, ";")
	for i = 0; i < len(tArr); i++ {
		aktIdArr := strings.Split(tArr[i], "|")
		aktArr := strings.Split(aktIdArr[1], ";")
		for j = 0; j < len(sArr); j++ {
			for n = 0; n < len(aktArr); n++ {
				if aktArr[i] == sArr[j] {
					num++
				}
			}
		}
		// совпало имя и список действий по кол-во и вхождению
		if num == len(sArr) && num == len(aktArr) {
			id, _ = strconv.Atoi(aktIdArr[0])
			break
		}
	}

	return id, NewName
}

/* Проверка совместимости списка базовых параметров внешнего бота с базой текущего */
func IsListActionToBot(txt string, typeArr int) bool {
	sArr := strings.Split(txt, ",")
	for _, val := range sArr {
		if IsCompareArrValue(val, typeArr) == false {
			return false
		}
	}

	return true
}

____________________________________________

BOT/brain/words_sensor/main_init.go
____________________________________________
package word_sensor


////////////////////////////////////////////////


// вызов из words_sensor.go
func afetrInitPhraseTree() {
	wordRecognizerInit() //
	// VerbalDetection("привет новая абзаца",1) // текст с пульта
	// PhraseSeparator("привет") // распознавание фразы
	// WordDetection("привет") // распознавание слова
	isReadyWordSensorLevel = 1
	initWordPult()
	initPrasePult()

// проверка
	//GetFirstSymbolFromWordID(556)
}

____________________________________________

BOT/brain/words_sensor/phrase_functions.go
____________________________________________
/*  функции для фраз */

package word_sensor

import (
	"BOT/lib"
	"regexp"
	"strconv"
	"strings"
)

// слово из ID узла дерева фраз
func GetWordFromPraseNodeID(nodeID int) string {
	if nodeID == 0 {
		return ""
	}
	//	ph := PhraseTreeFromID[nodeID]
	ph, ok := ReadePhraseTreeFromID(nodeID)
	if !ok {
		return ""
	}
	word := GetWordFromWordID(ph.WordID)

	return word
}

// []ID слов из ID узла дерева фраз
func GetWordsIDarrFromPraseNodeID(nodeID int) []int {
	if nodeID == 0 {
		return nil
	}
	//	ph := PhraseTreeFromID[nodeID]
	ph, ok := ReadePhraseTreeFromID(nodeID)
	if !ok {
		return nil
	}
	var reversID []int
	reversID = append(reversID, ph.WordID)
	for ph.ParentNode != nil {
		reversID = append(reversID, ph.ParentNode.WordID)
		if ph.ParentNode.ParentNode == nil {
			break
		}
		ph.ParentNode = ph.ParentNode.ParentNode
	}
	var out []int
	for i := len(reversID) - 1; i >= 0; i-- {
		if reversID[i] > 0 {
			out = append(out, reversID[i])
		}
	}

	return out
}

////////////////////////////////////////////////

// []ID слов из ID фразы
func GetWordsArrFromPraseID(praseID int) []int {
	var wArr []int
	for {
		//node := PhraseTreeFromID[lastID]
		node, ok := ReadePhraseTreeFromID(praseID)
		if !ok {
			break
		}
		wArr = append(wArr, node.WordID)
		praseID = node.ParentID
		if praseID == 0 {
			break
		}
	}

	return wArr
}

/////////////////////////////////////////////////////

// строка из ID фразы дерева фраз
func GetPhraseStringsFromPhraseID(lastID int) string {
	var idArr []string

	for {
		//node := PhraseTreeFromID[lastID]
		node, ok := ReadePhraseTreeFromID(lastID)
		if !ok {
			break
		}
		w := GetWordFromWordID(node.WordID)
		idArr = append(idArr, w)
		lastID = node.ParentID
		if lastID == 0 {
			break
		}
	}

	var str = ""
	for i := len(idArr) - 1; i >= 0; i-- {
		if len(str) > 0 {
			str += " "
		}
		str += idArr[i]
	}

	return str
}

/*
	выдать строку из массива wordsArr[]int

используется в update_genom.go
*/
func GetStrFromArrID(wArr []int) string {
	var out = ""

	for i := 0; i < len(wArr); i++ {
		out += GetWordFromWordID(wArr[i]) + " "
	}

	return out
}

// очистить фразу от неалфавитных символов
func ClinerNotAlphavit(prase string) string {
	var out = ""

	reg := regexp.MustCompile(`[а-я ]`)
	res := reg.FindAllString(prase, -1)
	for i := 0; i < len(res); i++ {
		out += res[i]
	}

	return out
}

// если есть такая фраза в Дереве, то выдать ее ID - ТОЛЬКО РАСПОЗНАВАНИЕ, А НЕ СОЗДАНИЕ НОВОЙ
// var WordsRecognisedIdArr []int //массив слов, не короче 4 символов
func GetExistsPraseID(text string) int {
	var id = 0
	//	WordsRecognisedIdArr = nil

	// чистим лишние пробелы
	rp := regexp.MustCompile("s+")
	text = rp.ReplaceAllString(text, " ")
	text = strings.TrimSpace(text)

	wordsArr := GetWordIDfromPhrase(text, true)
	//	WordsRecognisedIdArr = GetWordIDfromPhrase4limit(text, 4) //СЛОВА НЕ КОРОЧЕ 4-x символов
	wArr := strings.Split(text, " ")
	// проверяем, число слов wArr должно быть равно числу распознанных
	if len(wordsArr) != len(wArr) {
		return 0 // фраза не распознана
	}
	id = GetExistsPraseIDFromWordArr(wordsArr)

	return id
}

////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////

// если есть такая фраза в Дереве, то выдать ее ID - ТОЛЬКО РАСПОЗНАВАНИЕ, А НЕ СОЗДАНИЕ НОВОЙ
func GetExistsPraseIDFromWordArr(wordsArr []int) int {
	var id = 0
	if wordsArr == nil || len(wordsArr) == 0 {
		return 0 // фразы нет
	}
	str := PhraseDetection(wordsArr, true) // распознаватель фразы
	if len(str) > 0 {
		id = DetectedUnicumPhraseID
	}
	return id
}

/////////////////////////////////////////////////////////////////////

// удалить слово во всех упоминаниях в Дереве фраз
func deleteWordFromPhrase(wordID int) {
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/phrase_tree.txt")
	var out = ""
	var parentNewID = 0
	var parentOdID = 0

	for n := 0; n < len(strArr); n++ {
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		pID, _ := strconv.Atoi(p[1])
		//		node := PhraseTreeFromID[id]
		node, ok := ReadePhraseTreeFromID(id)
		if !ok {
			return
		}
		p = strings.Split(strArr[n], "|#|")
		wID, _ := strconv.Atoi(p[1])
		if wID == wordID {
			if len(node.Children) > 0 { // всем дочкам переписать родителей - node.ParentID
				parentNewID = node.ParentID
				parentOdID = node.ID
			} // если нет родителя, то можно просто удалить
			continue // не писать удаляемую строку
		}

		if pID > 0 && pID == parentOdID { // заменить родителя
			out += strconv.Itoa(id) + "|" + strconv.Itoa(parentNewID) + "|#|" + strconv.Itoa(wID) + "\r\n"
		} else {
			out += strArr[n] + "\r\n"
		}
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/phrase_tree.txt", out)
}

// вытащить первый символ из фразы
func GetFirstSymbolFromPraseID(PhraseID []int) int {
	if len(PhraseID) == 0 {
		return 0
	}
	// аналогично
	// GetPhraseStringsFromPhraseID(PhraseID[0])
	lastID := PhraseID[0]
	word := ""
	for {
		//		node := PhraseTreeFromID[lastID]
		node, ok := ReadePhraseTreeFromID(lastID)
		if !ok {
			break
		}

		word = GetWordFromWordID(node.WordID)
		lastID = node.ParentID
		if lastID == 0 {
			break
		}
	}
	if len(word) == 0 {
		return 0
	}
	r := []rune(word)
	first := GetSymbolIDfromRune(r[0])
	// проверка
	// w:=GetWordFromWordID(wID); if len(w)>0{}
	// s := GetSynbolFromID(first); if len(s) > 0 { }

	return first
}

/////////////////////////////////////////////

____________________________________________

BOT/brain/words_sensor/phrase_recognizer.go
____________________________________________
/* распознавание фразы
вербальная иерархия распознавателей

Распознавание фраз начинается в main.go с word_sensor.VerbalDetection(text_dlg, is_input_rejim, moodID)
Память о воспринятых фразах в текущем активном контексте (Vernike_detector.go): var MemoryDetectedArr []MemoryDetected
*/

package word_sensor

import (
	_ "strconv"
	_ "strings"
)

func iniPraseRecognising(){

}

// текущий уникальный ID последней активной ветки дерева - результат детекции фразы - для дальнейшего использования
var DetectedUnicumPhraseID=0
// нераспознанный остаток
var CurrentVerbalPhraseEnd []int
// текущий найденный ID последней активной ветки дерева слов
var DetectedCurrentPhraseID=0
// текущий номер слова распознаваемой фразы
var currentStepPhraseCount=0

/* переносим в дерево слов достаточно повторяющиеся из tempArr для trees_former.go
Распознать и вставить новое слово-фразу в дерево:
найти подходящий узел и если еще нет - вставить новый.
 */

/* проход одной фразы - распознавание ID слов фразы
noCreate true - не создавать новых узлов при распознавании в рефлексах и т.п.
 */
func PhraseDetection(words []int,noCreate bool) string {
	if len(words) == 0 { return "" }
	if len(words) == 1 && words[0] == 0 {	return ""	} // пустые строки не писать
	CurrentVerbalPhraseEnd = nil
	DetectedUnicumPhraseID = 0
	// var pultOut=""
	DetectedCurrentPhraseID = 0
	currentStepPhraseCount = len(words)
	r := words
	// основа дерева
	cnt := len(VernikePhraseTree.Children)
	for n := 0; n < cnt; n++ {
		phraseNode := VernikePhraseTree.Children[n]
		rt := phraseNode.WordID
		if r[0] == rt {
			cldrn := VernikePhraseTree.Children[n] //.Children
			getPhraseTreeNode(r, &cldrn)
		}
		if currentStepPhraseCount == 0 { break } // распознанно точно, не смотреть другие
	}
	// результат распознавания
	if DetectedCurrentPhraseID > 0 {
		if currentStepPhraseCount == 0 { // полностью распознан
			DetectedUnicumPhraseID = DetectedCurrentPhraseID
		} else {
			var nr = len(r) - currentStepPhraseCount
			CurrentVerbalPhraseEnd = r[nr:]
		}
	}
	var needSave = false
	if DetectedUnicumPhraseID == 0 {
		// нераспознанный остаток
		if len(CurrentVerbalPhraseEnd) > 0 {
			r := CurrentVerbalPhraseEnd
			var tree *PhraseTree
			if DetectedCurrentPhraseID > 0 {
//				tree = PhraseTreeFromID[DetectedCurrentPhraseID]
				node,ok:=ReadePhraseTreeFromID(DetectedCurrentPhraseID)
				if ok {
					tree =node
				}
			} else {
				tree = &VernikePhraseTree
			}
			// просто добавить новую ветку - из диалога это стоит делать за 1 раз т.к. слова уже известны
			if !noCreate{
				node := createNewNodePhraseTree(tree, 0, r[0])
				tree = node
				pt,ok:=ReadePhraseTreeFromID(tree.ID)
				if ok {
					id := createPhraseTreeNodes(r, pt)
					DetectedUnicumPhraseID = id
					needSave = true
				}
			}
		}
	}
	// нет вообще такого, добавить все слово
	if DetectedUnicumPhraseID == 0 {

		//tree := PhraseTreeFromID[0]
		tree,ok:=ReadePhraseTreeFromID(0)
		if ok {
			// сразу создать первый узел
			if len(r) > 0 && !noCreate {
				node := createNewNodePhraseTree(tree, 0, r[0])
				tree = node
				if tree != nil {
					tn,ok:=ReadePhraseTreeFromID(tree.ID)
					if ok {
						id := createPhraseTreeNodes(r, tn)
						DetectedUnicumPhraseID = id
						needSave = true
					}
				}
			}
		}
	}
	if needSave { SavePhraseTree() }
	out := GetPhraseStringsFromPhraseID(DetectedUnicumPhraseID)
	// заполнить PhraseTreeFromWordID
	finishScanAllTree()

	return out //pultOut+"{"+strconv.Itoa(DetectedUnicumPhraseID)+")"
}

// получить ID фразы - конечный узел дерева фраз
func getPhraseTreeNode(words []int, wt *PhraseTree) {
	if len(words) == 0 { return	}
	ost := words[1:]
	if words[0] != wt.WordID { return } // пошло не туда
	DetectedCurrentPhraseID = wt.ID
	currentStepPhraseCount = len(ost)

	for n := 0; n < len(wt.Children); n++ {
		getPhraseTreeNode(ost, &wt.Children[n])
	}

	return
}

// получить число узлов в ветке
func getNodeCountFromLastID(lastID int) int {
	if lastID == 0 { return 0	}
	var count = 0
	for {
//		node := PhraseTreeFromID[lastID]
		node,ok:=ReadePhraseTreeFromID(lastID)
		if !ok || node.WordID == 0 {
			break
		}
		count++
		lastID = node.ParentID
	}
	return count
}
____________________________________________

BOT/brain/words_sensor/phrase_tree.go
____________________________________________
/*  дерево фраз
вербальная иерархия распознавателей
Первый уровень дерева фраз может может заполняться любыми ID слов
Память о воспринятых фразах в текущем активном контексте (Vernike_detector.go): var MemoryDetectedArr []MemoryDetected
*/

package word_sensor

import (
	"BOT/lib"
	"regexp"
	"strconv"
	"strings"
)

// подошла очередь инициализации
func afterLoadPhraseArr(){
	loadPhraseTree()
	/*
	//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	SetNewPhraseTreeNode("повести и игра") //
	//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	SavePhraseTree()
	*/
	iniPraseRecognising()
	afetrInitPhraseTree()
	// для старых слов получить WordIdFormWord
	getWordIdFormWord()
	// deleteWordFromPhrase(11)
}

// дерево фраз, разбитых на слова, формат записи ID|ParentID|#|WordID
type PhraseTree struct {
	ID int 				// id узла слова
	ParentID int  // ID родителя
	WordID int 		// одно  слово, м.б. пробелорм или любым символом

	Children []PhraseTree 	// дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentNode *PhraseTree  // адрес родителя
}

// дерево фраз
var VernikePhraseTree PhraseTree

// карта поиска дерева фраз
//var PhraseTreeFromID = make(map[int]*PhraseTree)
var PhraseTreeFromID []*PhraseTree  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WritePhraseTreeFromID(index int, value *PhraseTree) {
	addPhraseTreeFromID(index)
	PhraseTreeFromID[index] = value
}
func addPhraseTreeFromID(index int){
	if index >= len(PhraseTreeFromID) {
		newSlice := make([]*PhraseTree, index+1)
		copy(newSlice, PhraseTreeFromID)
		PhraseTreeFromID = newSlice
	}
}
// считывание члена
func ReadePhraseTreeFromID(index int) (*PhraseTree,bool){
	if index<0{
		return nil,false
	}
	if index >= len(PhraseTreeFromID) || PhraseTreeFromID[index]==nil {
		return nil,false
	}
	return PhraseTreeFromID[index],true
}
/////////////////////////////////////////////////

// Последовательность wordID в ветке дерева нужно получать из GetWordArrFromPhraseID(PhraseID int)

///////////////////////////////////////////////////

/* ID фразы - по строке фразы - вместо GetExistsPraseID(str)
НО ВСЕ ФРАЗЫ ЗАПОМНАТЬ - КРУТОВАТО
Эта карта запоминается в отдельном файле потому как нет другой связки
(непросто получать при загрузке файла дерева фраз, точнее НУЖНО БУДЕТ ПОТРАТИТЬ ВРЕМЯ НА ВОССТАВЛЕНИЕ ФРАЗ ПО ID)

var PhraseIDFromPraseStr = make(map[string]int)
///// предотвращение ПАНИКИ КАРТ типа "concurrent map writes"
var PhraseIDFromPraseStrMapFlag=false //true- в карту идет запись
// перед чтением карты включать функцию уникального для данной карты арбитра MyMapCheck
func PhraseIDFromPraseStrMapCheck(){
	for PhraseIDFromPraseStrMapFlag{
		time.Sleep(10 * time.Microsecond)
	}
}
/////////////////////////////////
func savePhraseIDFromPraseStr() { // фраза|#|ID фразы
	var out = ""
	for str, id := range PhraseIDFromPraseStr {
		out+=str+"|#|"+id+"\r\n"

	}
	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_reflex/phrase_tree.txt", out)
	return
}
////////////////////////
func loadPhraseIDFromPraseStr() {

}
*/

/////////////////////////////////////////////////

// Все ID фраз по wordID: в каких ID фраз содержится данное слово
//var PhraseTreeFromWordID = make(map[int][]int)
var PhraseTreeFromWordID [][]int  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WritePhraseTreeFromWordID(index int, value []int) {
	addPhraseTreeFromWordID(index)
	PhraseTreeFromWordID[index] = value
}
func addPhraseTreeFromWordID(index int){
	if index >= len(PhraseTreeFromWordID) {
		newSlice := make([][]int, index+1)
		copy(newSlice, PhraseTreeFromWordID)
		PhraseTreeFromWordID = newSlice
	}
}
// считывание члена
func ReadePhraseTreeFromWordID(index int) ([]int,bool){
	if index >= len(PhraseTreeFromWordID) || PhraseTreeFromWordID[index]==nil {
		return nil,false
	}
	return PhraseTreeFromWordID[index],true
}
/////////////////////////////////////////////////

// для обеспечения уникальности узлов:
/*  лишнее
type PhraseUnicum struct {
	ID int
	wordID int
}
var PhraseUnicumIdStr=make(map[PhraseUnicum]int)// для каждого сочетания  выдается ID узла
*/

var lastPhraseTreeID = 0 // конечный узел дерева фраз

// создать новый узел дерева фраз
func createNewNodePhraseTree(parent *PhraseTree, id int, wordID int) *PhraseTree {
	if parent == nil {	return nil 	}
	//if wordID==0{ return nil }

	// после удаления слова - запрет на вставку новых слов до перезагрузки
	if blockingNewInsertWordAfterDeleted {
		lib.WritePultConsol("ПОСЛЕ УДАЛЕНИЯ СЛОВА - ЗАПРЕТ НА ВСТАВКУ НОВЫХ СЛОВ ДО ПЕРЕЗАГРУЗКИ")
		return nil
	}

	// notAllowScanInThisTime=true // запрет показа карты при обновлении
	if id == 0 {
		lastPhraseTreeID++
		id = lastPhraseTreeID
	} else {
		if lastPhraseTreeID < id {
			lastPhraseTreeID = id
		}
	}

	var newW PhraseTree
	newW.ID = id
	newW.ParentID = parent.ID
	newW.ParentNode = parent
	newW.WordID = wordID

	parent.Children = append(parent.Children, newW)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newTP *PhraseTree
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == newW.ID {
			newTP = &parent.Children[i]
		}
	}
	//!!!! PhraseTreeFromID[new.ID]=new  т.к. append меняет длину массива, перетусовывая адреса, то нужно:
	scanAllTree(parent) // здесь потому, что при загрузке из файла нужно на лету получать адреса
	/*if newTP != nil {
		WordsArrFromPhraseID[newTP.ID] = append(WordsArrFromPhraseID[newTP.ID], newTP.WordID)
		PhraseTreeFromWordID[newTP.WordID] = append(PhraseTreeFromWordID[newTP.WordID], newTP)
	}*/
	// notAllowScanInThisTime=false

	return newTP
}

// корректируем адреса всех узлов
func scanAllTree(parent *PhraseTree){
	updatingPhraseTreeFromID(parent)
}
func updatingPhraseTreeFromID(wt *PhraseTree) {
	if wt.ID > 0 {
//		wt.ParentNode = PhraseTreeFromID[wt.ParentID] // wt.ParentNode адрес меняется из=за corretsParent(,
		node,ok:=ReadePhraseTreeFromID(wt.ParentID)
		if ok {
			wt.ParentNode =node
	//		PhraseTreeFromID[wt.ID] = wt
			WritePhraseTreeFromID(wt.ID, wt)
		}
	}
	if wt.Children == nil {	return } // конец ветки
	for i := 0; i < len(wt.Children); i++ {
		updatingPhraseTreeFromID(&wt.Children[i])
	}
}



// Загрузка дерева фраз
func loadPhraseTree() {
	initPhraseTree(&VernikePhraseTree)
	// initPhraseTree(&VernikePhraseTree)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/phrase_tree.txt")
	cunt := len(strArr)
	// просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) < 2 {
			panic("Сбой загрузки дерева фраз: ["+strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p := strings.Split(strArr[n], "|#|")
		id, _ := strconv.Atoi(p[1])
		wordID := id
		if WordTreeFromID[wordID] == nil { continue } // нет такого узла дерева слов
		idP := strings.Split(p[0], "|")
		id, _ = strconv.Atoi(idP[0])
		parentID, _ := strconv.Atoi(idP[1])
		// новый узел с каждой строкой из файла
		node,ok:=ReadePhraseTreeFromID(parentID)
		if ok {
			createNewNodePhraseTree(node, id, wordID)
		}
	}

// заполнить PhraseTreeFromWordID
	finishScanAllTree()

	return
}
var curBrangeArr []int
func finishScanAllTree(){

//	PhraseTreeFromWordID = make(map[int][]int)
	PhraseTreeFromWordID=nil

	curBrangeArr=nil
	curScanAllTree(&VernikePhraseTree)
}
func curScanAllTree(wt *PhraseTree) {
	if wt.ID > 0 {
//		wt.ParentNode = PhraseTreeFromID[wt.ParentID] // wt.ParentNode адрес меняется из=за corretsParent(,
		node,ok:=ReadePhraseTreeFromID(wt.ParentID)
		if ok {
			wt.ParentNode = node
			curBrangeArr = append(curBrangeArr, wt.WordID)
		}
	}
	if wt.Children == nil {	// конец ветки
		/*if проверка не заблокирован ли уже {
			WordsArrFromPhraseIDmu.Lock()
			defer WordsArrFromPhraseIDmu.Unlock()
		}*/

		// перебрать все слова                       wordID - ЭТО НЕТ nodePhraseID !!!!!!!!!
		for i := 0; i < len(curBrangeArr); i++ {
			addPhraseTreeFromWordID(curBrangeArr[i])
			PhraseTreeFromWordID[curBrangeArr[i]] = append(PhraseTreeFromWordID[curBrangeArr[i]], wt.ID)
			/*
			val, ok := ReadePhraseTreeFromWordID(curBrangeArr[i])
				if (ok) {
					val = append(val, wt.ID)
				}*/
		}
		if len(curBrangeArr)>1{
			curBrangeArr=nil
		}

/* ЗАЧЕМ ТУТ условие есои по любому внизу curBrangeArr=nil???
		if len(PhraseTreeFromWordID[wt.ID])>1{
			curBrangeArr=nil
		}
 */
		curBrangeArr=nil
		return
	}
	for i := 0; i < len(wt.Children); i++ {
		//curBrangeArr=nil
		curScanAllTree(&wt.Children[i])
	}
}
////////////////////////////////////////////////////////////





/////////////////////////////////////////////////////////
// создать первый, нулевой уровень дерева
func initPhraseTree(vt *PhraseTree) {
	// createNewNodePhraseTree(vt,0,0)
	vt.ID = 0
	vt.WordID = 0

//	PhraseTreeFromID[vt.ID] = vt
	WritePhraseTreeFromID(vt.ID, vt)

	//updateWordTreeFromID()
	return
}

// Сохранить дерево фраз
// ID|ParentID|#|WordID
func SavePhraseTree() {
	var out = ""
	cnt := len(VernikePhraseTree.Children)
	for n := 0; n < cnt; n++ {
		out += getPtreeNode(&VernikePhraseTree.Children[n])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_reflex/phrase_tree.txt", out)
	return
}

// получить ветку дерева фраз в виде строки
func getPtreeNode(wt *PhraseTree) string {
	var out = ""

	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.ParentID) + "|#|"
	out += strconv.Itoa(wt.WordID) + "\r\n"

	if wt.Children == nil {	return out } // конец
	for n := 0; n < len(wt.Children); n++ {
		out += getPtreeNode(&wt.Children[n])
	}
	return out
}

/* вставка новой фразы со вставкой новых слов фразы,
так что фраза будет распознанна всегда.
 */
func SetNewPhraseTreeNode(phrace string) *WordTree {
	// чистим лишние пробелы
	rp := regexp.MustCompile("s+")
	phrace = rp.ReplaceAllString(phrace, " ")
	phrace = strings.TrimSpace(phrace)

	var wordsIDstr[] int // строка (не)распознанных слов

	/* сначала добавляем слова в дерево слов, потом - всю фразу в дерево фраз
	   Делим фразу на слова (в строке нет других разделительных символов,
	т.к. они уже сработали при разделении на фразы).
	*/
	wArr := strings.Split(phrace, " ")
	for n := 0; n < len(wArr); n++ { // перебор отдельных слов
		curWord := strings.TrimSpace(wArr[n])
		if len(curWord) == 0 { return nil	}

		id := SetNewWordTreeNode(curWord,false)
		// распознавание будет ВСЕГДА т.к. в случае новго слова оно вставляется в дерево слов тут же
		wordsIDstr = append(wordsIDstr, id)
	} //for n := 0; n < len(wArr); n++ { закончен проход отдельных слов
	// updateWordTreeFromID()// обновляем массив адресов узлов после всех append() родителей, меняющих адреса

	//  проход фразы
	// var needSave=false
	if len(wordsIDstr) > 0 {
		PhraseDetection(wordsIDstr,false)
		if DetectedUnicumPhraseID > 0 { // распознанная фраза
			CurrentPhrasesIDarr = append(CurrentPhrasesIDarr, DetectedUnicumPhraseID)

			//PhraseIDFromPraseStrMapFlag = true
			//PhraseIDFromPraseStr[phrace] = DetectedUnicumPhraseID
			//PhraseIDFromPraseStrMapFlag = false
		}
		// Запись недостающего в дерево фраз происходит в PhraseDetection(wordsIDstr)
	}
	// if needSave {
	//	savePhraseTree()
	// }
	return nil
}

// создание ветки фраз, начиная с заданного узла
func createPhraseTreeNodes(word []int, wt *PhraseTree) int {
	ost := word[1:]
	if len(ost) == 0 { return wt.ID	}

	pn,ok:=ReadePhraseTreeFromID(wt.ID)
	if ok {
		node := createNewNodePhraseTree(pn, 0, ost[0])
		pn2,ok2:=ReadePhraseTreeFromID(node.ID)
		if ok2 {
			id := createPhraseTreeNodes(ost, pn2)
			return id
		}
	}
return 0
}
____________________________________________

BOT/brain/words_sensor/phrase_tree_pult.go
____________________________________________
/* выдать дерево фраз на Пульт по GET-запросу http://go:8181?get_phrase_tree=1
Запросы даются при запуске и при изменении размера файла дерева /memory_reflex/word_tree.txt
 */

package word_sensor

import "strconv"

func initPrasePult() {
	//str:=GetPhraseTreeForPult()
	//if len(str)>0{}
}

// образ дерева фраз для вывода на Пульт
var praseTreeModel = ""

// проход дерева фраз
func GetPhraseTreeForPult() string {
	if notAllowScanInThisTime { return "!!!" }
	praseTreeModel = ""
	scanPraseNodes(-1, &VernikePhraseTree)

	return praseTreeModel
}

// сканировать узел дерева фраз
func scanPraseNodes(level int,wt *PhraseTree) {
	if wt.ID > 0 {
		praseTreeModel += setShift(level)
		// получить слово из ID конечного узла дерева слов
		praseTreeModel += GetWordFromWordID(wt.WordID) + "(" + strconv.Itoa(wt.ID) + " WordID="+strconv.Itoa(wt.WordID)+")<br>\r\n"
	}
	level++
	for n := 0; n < len(wt.Children); n++ {
		scanPraseNodes(level,&wt.Children[n])
	}
}

// отступ
func setShift(level int) string {
	var sh = ""
	for n := 0; n < level; n++ {
		sh += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	}
	return sh
}
____________________________________________

BOT/brain/words_sensor/trees_former.go
____________________________________________
/*  Формирователь Дерева слов и Дерева фраз при наборе текстов
из редактора http://go/pages/words.php
и при общении с Beast с Пульта http://go/pult.php
*/

package word_sensor

import (
	"BOT/lib"
	"sort"
	"strconv"
	"strings"
)

/* переносим в дерево слов достаточно повторяющиеся из tempArr
 limitWord - число повторений отдельных слов, с которых начинается пеернос.
limitFraze - число повторений отдельных фраз (с несколькими словами), с которых начинается пеернос.
Для автозаливки - (4,6),
для сообщения с Пульта - (2,4)
Фомат words_temp_arr.txt: Число повторений|#|Фраза|#|число сохранений (старость)
*/
//var curSizeWordsTempArrTxt=0
func UpdateWordTreeFromTempArr(limitWord int, limitFraze int) {
	// return

	loadTempArr()// вский раз получать обновленный массив накоплений слов

	// Удалять использованные строки накопительного массива
	var newTempFileStr = "" // это будет записано после процесса в /memory_reflex/words_temp_arr.txt
	var str[]string // для выбранных повторяющихся слов - чтобы потом отсортирвать
	// чтобы фразы могли использовать слова (минимизация размера дерева)
	// слово или фраза?
	var existsWords = false
	var existsPhrase = false

	// сначала прходим только слова, потом - только фразы,
	lib.MapCheckBlock(MapGwardWordTempArr)
	for k, v := range tempArr {
		sps := strings.Split(k, " ")
		if len(sps) == 1 { // это слово
			if v[0] >= limitWord {
				str = append(str, k)

			} else {
				if v[1] > 1000 { // старое забывать, - очистка мусора и давно не воспринимаемого
					continue
				}
//оставляем строку со словом, но добавляем ее старость: strconv.Itoa(v[1]+1)
				newTempFileStr += strconv.Itoa(v[0]) + "|#|" + k + "|#|" + strconv.Itoa(v[1]+1) + "\r\n"
			}
		}
	}
	lib.MapFree(MapGwardWordTempArr)

	/* str - список слов, повторившихся в memory_reflex/words_temp_arr.txt более limitWord раз
	Распознать их в сенсоре, дополнив базу слов.
	 */
	//oldNCh:=NoCheckWordCount
	//NoCheckWordCount=true
	sort.Strings(str) // по алфавиту, чтобы максимально облегчить последовательное разделение слов
	for i := 0; i < len(str); i++ {
		cur := str[i]
		SetNewWordTreeNode(cur,false)
		SetNewPhraseTreeNode(cur)
		existsWords = true
	}
	//NoCheckWordCount=oldNCh
	if existsWords {
		SaveWordTree()
		//	SavePhraseTree()// для формирования WordIdFormWord[]
		existsPhrase = true
	}

	// проход для фраз
	str = nil
	lib.MapCheckBlock(MapGwardWordTempArr)
	for k, v := range tempArr {
		sps := strings.Split(k, " ")
		if len(sps) > 1 { // это фраза
			if v[0] >= limitFraze {
				str = append(str, k)
			} else {
				if v[1] > 1000 { // старое забывать, - очистка мусора и давно не воспринимаемого
					continue
				}
				//оставляем строку с фразой, но добавляем ее старость: strconv.Itoa(v[1]+1)
				newTempFileStr += strconv.Itoa(v[0]) + "|#|" + k + "|#|" + strconv.Itoa(v[1]+1) +  "\r\n"
			}
		}
	}
	lib.MapFree(MapGwardWordTempArr)

	// на распознавание фраз
	sort.Strings(str) // по алфавиту, чтобы максимально облегчить последовательное разделение слов
	for i := 0; i < len(str); i++ {
		cur := str[i]
		SetNewPhraseTreeNode(cur)
		existsPhrase = true
		// SaveWordTree() // для пошагового контроля
		// if(i>1){}
	}
	if existsPhrase {
		SavePhraseTree()
	}

	// Удаление использованных строк накопительного массива: запись только незатронутых
	lib.WriteFileContentExactly(lib.GetMainPathExeFile() + "/memory_reflex/words_temp_arr.txt", newTempFileStr)
}
____________________________________________

BOT/brain/words_sensor/Vernike_detector.go
____________________________________________
/*  распознаватель слов и фраз по типу зоны Вернике в мозге.
Память о воспринятых фразах в текущем активном контексте (Vernike_detector.go): var MemoryDetectedArr []MemoryDetected

Распознавание фраз начинается в main.go с word_sensor.VerbalDetection(text_dlg, is_input_rejim, moodID)
С ПУльта приходит текст, который в VerbalDetection() разбирается на фрзацы (\r\n):

абзацы в PhraseSeparator() разбираются на фразы по разделителям (знаки препинания)
фразы в WordDetection() разбиваются на слова.
Распознанные (и нераспознанные) последовательности сохраняются в оперативной памяти Beast MemoryDetectedArr.
где распознанный текст представлен в виде уникального laslID фразы

ОПИСКИ при вводе слова. Если слово не распознается и оно имеет более 3-х символов,
то делается предположение об описке внутренних символов
(в природном распознавателе слово узнается если точно совпали первая и последняя буквы,
а внутренние буквы могут быть как угодно перемешаны)
Если слово распознается, то подставляется ID слова.

Нераспознанной фразы НЕ БЫВАЕТ т.к. она тут же создается

Тон фразы можно задать 1) с помощью знаков ! и ? в конце фразы
или задать преимущественно - выбрав Тон под окном ввода фразы.
*/

package word_sensor

import (
	_ "BOT/lib"
	"regexp"
	"strconv"
	"strings"
)

// запрет показа карт WordTreeFromID и PhraseTreeFromID во время распознавания и записи
// против паники типа "одновременная запись и считывание карты"
var notAllowScanInThisTime = false

// индикация, что дерево загружено, можно вводить тексты
var isReadyWordSensorLevel = 0

func IsReadyWordSensorLevel() bool {
	if isReadyWordSensorLevel > 0 {
		return true
	} // связь с корнями проекта
	return false
}

// word_sensor.VerbalDetectin("активностный")
// для использования в SetNewWordTreeNode

/*
	в CurrentPhrasesIDarr набирается массив lastID распознанных фраз для дерева автоматизмов

При активации дерева автоматизмов:
if len(wordSensor.CurrentPhrasesIDarr) > 0 { ...

Если CurrentPhrasesIDarr[n]==-1 - фраза есть, но она нераспознана.
*/
var CurrentPhrasesIDarr []int
var CurretWordsIDarr []int // массив словID и вместо нераспознанного -1

// тон сообщения с Пульта при передаче фразы
var CurPultTone = 0

// настроение с Пульта при передаче фразы
var CurPultMood = 0

// текущий тон фразы: 0-обычный, 1-восклицательный, 2-вопросительный
var DetectedTone = 0

/* Память о воспринятых фразах в текущем активном контексте:
В принципе это не нужно, т.к. имеется эпизодическая память
(правда только для Правил, но это как раз и всязывает фразы с их полезностью)

type MemoryDetected struct {
	//распознанный текст в виде lastID выделенных фраз
	PhrasesID []int // массив структур распознанных фраз
	Tone      int   // Тон: 0-обычный, 1-восклицательный, 2-вопросительный, 3-вялый, 4-Повышенный
	Mood      int   // настроение при передаче фразы с Пульта: 20-Хорошее 21-Плохое 22-Игровое 23-Учитель 24-Агрессивное 25-Защитное 26-Протест
	// индекс==ID активного базового контекста, значение - вес этого контекста
	ActveContextWeight map[int]int
}

// добавить строку в массив памяти о воспринятых фразах в текущем активном контексте
func addNewMemoryDetected() {
	var newM MemoryDetected
	newM.PhrasesID = CurrentPhrasesIDarr
	// тон может указываться 1) в виде ! или ? во фразе - DetectedTone 	И/ИЛИ 2) в виде радиокнопки Тон с Пульта - CurPultTone
	var tone = 0
	if DetectedTone > 0 { // преимущество - у задатчика тона 2)
		tone = DetectedTone
	} else { // есть ! или ? во фразе
		tone = CurPultTone
	}
	newM.Tone = tone
	newM.Mood = CurPultMood // настроение с Пульта: повышенный нормальный вялый Хорошее Плохое Игровое Учитель Агрессивное Защитное Протест
	newM.ActveContextWeight = gomeostas.GetActiveContextInfo()
	MemoryDetectedArr = append(MemoryDetectedArr, newM)
}

// массив памяти накапливается в течении дня, обрабатывается и очищается во сне
var MemoryDetectedArr []MemoryDetected
*/

/*  распознавание фразы с Пульта - бывает только в нижнем регистре
 */
var wlev = 0

// // если обычный режим диалога (на ПУльте не стоит галка "набивка рабочих фраз без отсеивания мусорных слов ")
var NoCheckWordCount = false // is_input_rejim - набивка рабочих фраз с отсеиванием мусорных слов

/*
	вызывается фразой с Пульта

text="привет|!|#|хочу| |научить| |тебя| |простым| |вещам|.|#|для| |начала| |давай| |усвоим| |длинные| |тексты|.|#|в| |них| |могут| |попадаться| |знакомые| |значащие| |слова| |вроде| |улыбнуться|,| |давай| |учить|,| |бестолочь|.|#|ты| |должен| |найти| |их|,| |выделить| |правила| |и| |обобщить| |слова| |в| |ответное| |общее| |действие|.|"

func UpdateWordTreeFromTempArr //переносим в дерево слов достаточно повторяющиеся из tempArr
NoCheckWordCount = true //игнорировать getWordTemparrCount и всегда распознавать слова
*/
var NeedCheckTempList = false // был ввод фразы с пульта - обработать в pula.go func pulsActions() в свободное время.
func VerbalDetection(text string, noFilterInput int, toneID int, moodID int) string {
	NeedCheckTempList = true
	notAllowScanInThisTime = true // запрет показа карты при обновлении
	NoCheckWordCount = false
	CurrentPhrasesIDarr = nil
	CurretWordsIDarr = nil
	if noFilterInput == 0 { // 0 - это набивка рабочих фраз без отсеивания мусорных слов
		// игнорировать getWordTemparrCount и всегда распознавать слова
		NoCheckWordCount = true
	}
	CurPultTone = toneID
	CurPultMood = moodID

	pultOut := ""

	// стандартно разделить текст на короткие фразы, отправить на накопление в /memory_reflex/words_temp_arr.txt
	// разделяем на фразы, получая strArr: "привет| |о| |тупой"
	strArr := strings.Split(text, "|#") // а не |#| - чтобы оставлять разделитель "|"
	for i := 0; i < len(strArr); i++ {
		//if i > 0 { pultOut += "<br>" }

		// добавление в файл /memory_reflex/words_temp_arr.txt накопления новых слов и фраз
		if !NoCheckWordCount { // накапливать статистику если не задан режим распознавания сразу
			praseID, pultOut := addNewtempArr(strArr[i])
			if praseID == 0 { // фраза нераспознана
				/*	м.б. в таком случае подставлять наиболее похожую фразу, как это сделал бы персептрон при распознавании
					но смысл изменяется буквально он любого слова и знака, так что не нужно, просто - сигнал НЕ ПОНЯЛ
				*/
				CurrentPhrasesIDarr = append(CurrentPhrasesIDarr, -1)
			}
			// в func UpdateWordTreeFromTempArr() будут учтены новые слова и фразы, встратившиеся достаточное число раз
			// ответ на Пульт:
			notAllowScanInThisTime = false
			return pultOut
		} else { // распознавать за 1 раз - просто проход фразы с распознаванием

			text0 := strings.Replace(strArr[i], "|", "", -1)
			pultOut += PhraseSeparator(text0)
			if DetectedUnicumPhraseID > 0 { // распознанная фраза
				CurrentPhrasesIDarr = append(CurrentPhrasesIDarr, DetectedUnicumPhraseID)
				pultOut += "<br>"
				//return pultOut
			}
			// ответ на Пульт:
			// но нераспознанной фразы НЕ БЫВАЕТ т.к. она тут же создается
			//return "Фраза не распознана."
		}
		//pultOut += "<br>"
	} //for i := 0; i < len(strArr); i++ {
	// добавить в стек памяти распознанных
	//addNewMemoryDetected()

	// reflexes.ActiveFromPhrase() // активировать дерево рефлексов фразой - только для условных рефлексов
	// ответ на Пульт:
	//	notAllowScanInThisTime = false
	return pultOut
}

// проход одной фразы (т.е. по разделителям в предложении, а не по \r\n)
func PhraseSeparator(text string) string {
	var pultOut = ""
	// чистим лишние пробелы
	rp := regexp.MustCompile("s+")
	text = rp.ReplaceAllString(text, " ")
	text = strings.TrimSpace(text)
	wordsArr := GetWordIDfromPhrase(text, false) // распознаватель слов
	//	time.Sleep(1000 * time.Microsecond)
	str := PhraseDetection(wordsArr, false) // распознаватель фразы
	if DetectedUnicumPhraseID > 0 {
		pultOut += str + "(" + strconv.Itoa(DetectedUnicumPhraseID) + ")"
	}

	// тон сообщения
	DetectedTone = 0
	if strings.Contains(text, "!") {
		DetectedTone = 1
	}
	if strings.Contains(text, "?") {
		DetectedTone = 2
	}

	return pultOut
}

/*
	получить последователньость wordID из уникального идентификатора фразы CurrentPhrasesIDarr[i]

начиная с любого узла дерева Фраз (не обязательно конечного!) - к первому узлу ветки
*/
func GetWordArrFromPhraseID(PhraseNodeID int) []int {
	var wArr []int
	// пройти фразу от последнего слова до первого
	//	w:=PhraseTreeFromID[PhraseNodeID]
	w, ok := ReadePhraseTreeFromID(PhraseNodeID)
	if !ok {
		return nil
	}
	wArr = append(wArr, w.ID)
	for w.ParentID > 0 {
		pht, ok := ReadePhraseTreeFromID(w.ParentID)
		if ok {
			wArr = append(wArr, pht.ID)
			w = pht
		}
	}

	// нельзя так делать, уходит в бесконечный цикл: обращение к w.ID возвращает предыдущее значение
	/*	for w.ParentID > 0 {
		//w=PhraseTreeFromID[w.ParentID]
		w, ok := ReadePhraseTreeFromID(w.ParentID)
		if ok {
			wArr = append(wArr, w.ID)
		}
	}*/
	// восстановить порядок слов
	var wordIDarr []int
	for i := len(wArr) - 1; i >= 0; i-- {
		wordIDarr = append(wordIDarr, PhraseTreeFromID[wArr[i]].WordID)
	}
	return wordIDarr
}

////////////////////////////////////////

____________________________________________

BOT/brain/words_sensor/word_functions.go
____________________________________________
/* функции для распознаваемых слов */

package word_sensor

import (
	"BOT/lib"
	"strconv"
	"strings"
)

// получить слово из ID слова (из ID конечного узла дерева слов)
func GetWordFromWordID(lastID int) string {
	var idArr []string
	for {
		node := WordTreeFromID[lastID]
		if node == nil || lastID == 0 {
			break
		}
		idArr = append(idArr, node.Symbol)
		lastID = node.ParentID
	}

	var str = ""
	for i := len(idArr) - 1; i >= 0; i-- {
		str += idArr[i]
	}

	return str
}

// вытащить первый символ из слова
func GetFirstSymbolFromWordID(wordID int) int {
	word := GetWordFromWordID(wordID)
	if len(word) == 0 {
		return 0
	}
	r := []rune(word)
	first := GetSymbolIDfromRune(r[0])

	return first
}

/*
// получить первый символ из ID слова (из ID конечного узла дерева слов)
func GetFirstSymbolFromWordID(lastID int) int {
	var lastN *WordTree
	for {
		node := WordTreeFromID[lastID]
		if node == nil || lastID == 0{
			sID,_:=strconv.Atoi(lastN.Symbol)
			r:=rune(sID)
			return GetSymbolIDfromRune(r)
		}
		lastID = node.ParentID
		lastN=node
	}

	return 0
}*/

/*
	выдать массив wordsArr[]int из фразы (а не абзаца или текста!)

использовать ТОЛЬКО ДЛЯ func PhraseSeparator !!!
*/
func GetWordIDfromPhrase(phrase string, noCreate bool) []int {
	var out []int
	/*  Делим фразу на слова (в строке нет других разделительных символов,
	т.к. они уже сработали при разделении на фразы).
	*/
	wArr := strings.Split(phrase, " ")
	for n := 0; n < len(wArr); n++ { // перебор отдельных слов
		curWord := strings.TrimSpace(wArr[n])
		wID := SetNewWordTreeNode(curWord, noCreate)
		if wID > 0 {
			out = append(out, wID)
		}
	}
	return out
}

/*
выдать массив wordsArr[]int из фразы
СЛОВА НЕ КОРОЧЕ limit символов

func GetWordIDfromPhrase4limit(phrase string, limit int) []int {
	var out []int
	wArr := strings.Split(phrase, " ")
	for n := 0; n < len(wArr); n++ { // перебор отдельных слов
		curWord := strings.TrimSpace(wArr[n])
		if len(curWord) < limit {
			continue
		}
		wID := SetNewWordTreeNode(curWord, true)
		if wID > 0 {
			out = append(out, wID)
		}
	}
	return out
}*/
/////////////////////////////////////////////////////////////////////

/*
	для старых слов получить WordIdFormWord - для распознавания неточно введенных слов и т.п.

Проход всего дерева фраз - там выделены известные слова.
Запускачется при нициализации Дерева фраз,
а при вставки новых слов в Дерево слов - сразу заполняется WordIdFormWord
*/
func getWordIdFormWord() {
	for _, ph := range PhraseTreeFromID {
		if ph == nil {
			continue
		}
		word := GetWordFromWordID(ph.WordID)
		WordIdFormWord[word] = ph.WordID
	}
	return
}

/*
	Cлово проверяется на наличие в списке старых (до сохранения) слов WordIdFormWord=make(map[string]int)

и если оно есть, то возвращается его ID.
Если слова нет и оно имеет более 4-х символов, то делается предположение об описке внутренних символов
(в природном распознавателе слово узнается если точно совпали первая и последняя буквы,
а внутренние буквы могут быть как угодно перемешаны)
Если слово распознается, то возвращается ID слова.
*/
func tryWordRecognize(word string) int {
	id := WordIdFormWord[word]
	if id != 0 {
		return id
	}
	id = getAlternative(word)
	if id != 0 {
		return id
	}

	return 0
}

var blockingNewInsertWordAfterDeleted = false // после удаления - запрет на вставку новых слов до перезагрузки
/*
	удалить слово с ID из дерева слов

Пройти от данного ID по его родителям до ID предыдущего слова.
Так, при удалении "приветствую" должны удаляться узлы чтобы осталось "привет".
После этого удаляются это слово из Дерева Фраз.

!После удаления ряда слов нужно перезагрузить сервер чтобы обновились данные!
*/
func DeleteWord(wID int) {
	node := WordTreeFromID[wID]
	if node == nil {
		return
	} // уже удалена
	wordID := wID // сохраняем для удаления в Дереве фраз
	// проходим ветку дерева
	for node.ParentNode != nil {
		// это или вложенное слово или ветвление на другое слово
		if existsWordID(node.ParentNode.ID) || len(node.ParentNode.Children) > 1 { // начинается уже другое, вложенное слово
			// удаляем узел прямо из файла word_tree.txt
			deleteNodeFromFile(node.ID)
			// удалить слово все всех упоминаниях в Дереве фраз
			deleteWordFromPhrase(wordID)
			return
		}
		next := node.ParentNode
		// удаляем узел прямо из файла word_tree.txt
		deleteNodeFromFile(node.ID)
		blockingNewInsertWordAfterDeleted = true
		node = next
	}
	return
}

// есть ли слово с данным ID
func existsWordID(ID int) bool {
	for _, k := range WordIdFormWord {
		if k == ID {
			return true
		}
	}
	return false
}

// удалить строку из word_tree.txt
func deleteNodeFromFile(wID int) {
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/word_tree.txt")
	var out = ""
	for n := 0; n < len(strArr); n++ {
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		if id == wID {
			continue
		}
		out += strArr[n] + "\r\n"
	}
	lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_reflex/word_tree.txt", out)
}

____________________________________________

BOT/brain/words_sensor/word_recognizer.go
____________________________________________
/*
Распознаватель слов по символьно для заполнения дерева слов word_tree.go
и распознавания слов при вводе с пульта
*/

package word_sensor

import (
	"BOT/lib"
	"fmt"
	"strings"
)

func wordRecognizerInit() {
	//	WordDetection("играть") // распознавание слова
}

// текущий уникальный ID последней активной ветки дерева - результат детекции фразы - для дальнейшего использования
var DetectedUnicumID = 0
var FirstSimbolID = 0 			// при активации дерева слов
var CurrentVerbalEnd []rune // нераспознанный остаток
var detectedCurrentID = 0 	// текущий найденный ID последней активной ветки дерева слов
var currentStepCount = 0  	// текущеее число нераспознанных символов

/* var lastFoundID=0 // последний ID символа при проходе дерева
проход одного слова - распознавание слова
возвращает найденное ID слова или похожей альтернативы

noCreate true - не создавать новых узлов при распознавании в рефлексах и т.п.
 */
func WordDetection(word string,noCreate bool) int {
	word = strings.TrimSpace(word)
	if len(word) == 0 {	return 0 }

	CurrentVerbalEnd = []rune("")
	DetectedUnicumID = 0
	// var pultOut=""
	detectedCurrentID = 0
	currentStepCount = 0

	// попробовать найти подходящее слово по готовому массиву ID слов
	DetectedUnicumID = tryWordRecognize(word)
	if DetectedUnicumID > 0 {
		return DetectedUnicumID
	}

/* Дальше пойдет только если на пульте включен "режим форсированной обработки".
Иначе слова заходят в дерево при накоплении в words_temp_arr.txt
	if !NoCheckWordCount {
//		updateWordTreeFromTempArr(2,4)
		return 0
	}
	*/
	r := []rune(word)
	// основа дерева
	cnt := len(VernikeWordTree.Children)
	var curFirstLevelID = 0
	for n := 0; n < cnt; n++ {
		smblNode := VernikeWordTree.Children[n]
		rt := []rune(smblNode.Symbol)
		if r[0] == rt[0] {
			FirstSimbolID = GetSymbolIDfromRune(r[0])
			curFirstLevelID = smblNode.ID
			if len(r) == 1 { // это - символ, присвоить слову ID символа
				DetectedUnicumID = VernikeWordTree.Children[n].ID
				CurrentVerbalEnd = []rune("")
				return DetectedUnicumID
			}

			cldrn := VernikeWordTree.Children[n].Children
			cnt := len(cldrn)
			for n := 0; n < cnt; n++ {
				getWordTreeNode(r[1:], &cldrn[n])
			}
		}
	}
	// результат распознавания
	if detectedCurrentID > 0 {
		if currentStepCount == 0 { // полностью распознан
			DetectedUnicumID = detectedCurrentID
		} else {
			var nr = len(r) - currentStepCount
			CurrentVerbalEnd = r[nr:]
		}
	}

	var needSave = false
	if DetectedUnicumID == 0 {
		/* лучше это делать до прохода дерева потому как сравнивается только со старыми словами, переместил
		// если обычный режим диалога (на ПУльте не стоит галка "набивка рабочих фраз без отсеивания мусорных слов ")
		if !NoCheckWordCount {
			//отсеивать мусорных (редких - менее 4 повторов в tempArr) слов
			repeet := getWordTemparrCount(text)
			if repeet < 4 {
				// попробовать найти подходящее слово
				DetectedUnicumID = getAlternative(text)
				if DetectedUnicumID != 0 {
					return DetectedUnicumID
				}
			}
		}
		*/

		// нераспознанный остаток
		if len(CurrentVerbalEnd) > 0 {
			r := CurrentVerbalEnd
			var tree *WordTree
			if detectedCurrentID > 0 {
				tree = WordTreeFromID[detectedCurrentID]
			} else {
				tree = &VernikeWordTree
			}
			// просто добавить новую ветку - из диалога это стоит делать за 1 раз т.к. слова уже известны
			if !noCreate {
				id := createWordTreeNodes(r, tree)
				DetectedUnicumID = id
				WordIdFormWord[word] = id // добавить в список слов
					// SaveWordTree()
				needSave = true
			}
		}
	}

	// нет вообще такого, добавить все слово, пройдя первый символьный уровень дерева
	if DetectedUnicumID == 0 {
		tree := WordTreeFromID[curFirstLevelID]//пройдя первый символьный уровень дерева
		ost:=r[1:]
		if len(ost) > 0 && !noCreate  {
				// добавить в дерево остальные символы, начиная с узла node
			id := createWordTreeNodes(ost, tree)//WordTreeFromID[tree.ID]
			DetectedUnicumID = id
			WordIdFormWord[word]=id // добавить в список слов
				//SaveWordTree()
			needSave = true
		}
	}
	if needSave {
		SaveWordTree()
	}

	return DetectedUnicumID //pultOut+"{"+strconv.Itoa(DetectedUnicumID)+")"
}

// cканирование следует строго по нужной ветке
func getWordTreeNode(word []rune, wt *WordTree) {
	if len(word) == 0 {	return }

	ost := word[1:]
	if string(word[0]) != wt.Symbol {	return } // пошло не туда

	detectedCurrentID = wt.ID
	currentStepCount = len(ost)
	for n := 0; n < len(wt.Children); n++ {
		getWordTreeNode(ost, &wt.Children[n])
	}
}

/* найти слово в tempArr и выдать его повторяемость, если слова нет в tempArr - добавить его.
 слово еще раньше добавляется в tempArr если его там нет
так что оно уже обязательно там будет
*/
func getWordTemparrCount(word string) int {
	lib.MapCheckBlock(MapGwardWordTempArr)
	for k, v := range tempArr {
		if k == word {
			lib.MapFree(MapGwardWordTempArr)
			return v[0]
		}
	}
	lib.MapFree(MapGwardWordTempArr)
	return 0 // на всякий случай
}

/* ранее - не найдено в WordIdFormWord[word]
попробовать найти подходящее слово с альтрнативным ID
Первые буквы должны совпадать, а остальные, кроме последней (разные окончания),
быть перемешаны, но в наличии >80%.
Сканирует дерево с начальной буквы строго по числу чимволов слова.
Это имитирует свойство персептронного распознавателя.
*/
func getAlternative(word string) int {
	defer func(){ // ловим панику
		if err := recover(); err != nil {
			fmt.Println(err) // просто выдать сообщение чтобы выловить с breakpointe здесь и пройти по стеку
		}
	}()
	rw := []rune(word)
	var rwLen = len(rw)
	if rwLen < 4 { return 0 }
	// выбрать известные слова с первой и последней буквой как у word
	var wArr = make(map[int][]rune)
	for w, id := range WordIdFormWord {
		r := []rune(w)
		rLen := len(r)
		if rLen < 3 || r[0] != rw[0] || rLen != rwLen { continue }
		if rw[rwLen - 1] == r[rLen - 1] {
			r0 := r[1:]
			r0 = r0[:(rLen - 1)]
			wArr[id] = r0
		}
	}
	if len(wArr) == 0 { return 0 }
	// проверять все ли внутренние буквы совпадают
	rw0 := rw[1:]
	rw0 = rw0[:(rwLen - 1)]
	for id, r := range wArr {
		// сопоставляются только внутренние части слов (без первой и последней букв)
		if isEquivalented(r, rw0) { return id	}
	}

	return 0
}

// все внутренние буквы должны присуствовать
func isEquivalented(r1 []rune,r2 []rune) bool {
	for n := 0; n < len(r1); n++ {
		var isAbsent = 1
		for m := 0; m < len(r2); m++ {
			if r1[n] == r2[m] {
				isAbsent = 0
				break
			}
		}
		if isAbsent == 1 { return false }
	}
	return true
}
____________________________________________

BOT/brain/words_sensor/word_tree.go
____________________________________________
/* дерево символов, слов
вербальная иерархия распознавателей
*/

package word_sensor

import (
	"BOT/lib"
	"strconv"
	"strings"
)

// Первый уровень дерева - символы. Заглавных не может быть в воспринимаемом
var symbolsArr = []string{" ","а","б","в","г","д","е","ё","ж","з","и","й","к","л","м","н","о","п","р","с","т","у","ф","х","ц","ч","ш","щ","ъ","ы","ь","э","ю","я","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","!","?","@","#","$","%","^","&","*","(",")","+","=","-","1","2","3","4","5","6","7","8","9","0","[","]","{","}","<",">",".",",","/"}
var symbolsRuneArr = make([]rune, len(symbolsArr)) // []rune

func GetSynbolFromID(id int) string {
	if len(symbolsArr) < id { return ""	}
	return symbolsArr[id]
}

// для получения ID symbolsArr по букве (по string символа)
var SymboIDfromSymbl = make(map[string]int)
// для получения ID symbolsArr по rune символа)
var SymboIDfromRune = make(map[rune]int)

func IsSymbol(w string) bool {
	// s:=[]rune(w)
	for i := 0; i < len(symbolsArr); i++ {
		if w == symbolsArr[i] { return true	}
	}
	return false
}

func IsАlphabeticSymbol(w string) bool {
	var alphabeticEnd = 60 // до начала неалфавитных символов
	for i := 1; i < alphabeticEnd; i++ {
		if w == symbolsArr[i] { return true	}
	}
	return false
}

// word_sensor.GetSymbolID(s string)
func GetSymbolID(s string) int {
	/*
	for i := 0; i < len(symbolsArr); i++ {
		if s == symbolsArr[i]{
			return i
		}
	}
	return 0
	 */
	return SymboIDfromSymbl[s]
}

// для получения ID symbolsArr по rune символа  не работает
func GetSymbolIDfromRune(r rune) int {
/*
	for i := 0; i < len(symbolsArr); i++ {
		sr:=rune(symbolsArr[i][0])
		if  sr== r{
			return i
		}
	}
	return 0
	*/
	return SymboIDfromRune[r]
}

// дерево слов, разбитых на символы
/*у алфавитных узлов (каждый узел - 1 символ) проявился недостаток:
в дереве слов невозможно вычленить реальные слова,
например, слово "приветствую" перекрывает веткой слово "привет".
Но в дереве фраз все слова имеют ID узла дерева слов, так что список старых (сохраненных) слов
формируется в виде WordIdFormWord=make(map[string]int) проходом в func getWordIdFormWord()
*/
type WordTree struct {
	ID int 								// id узла символа
	Symbol string 				// один символ
	Children []WordTree 	// дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID int     			// ID родителя
	ParentNode *WordTree  // адрес родителя
}

var VernikeWordTree WordTree // дерево слов
var WordTreeFromID = make(map[int]*WordTree) // узел дерева от его ID
var WordTreeFromStr = make(map[string][]*WordTree) // массив узлов с такой SymbolID
//var WordFromID=make(map[int]string) лучше не пытаться получать это при загрузке дерева, используем GetWordFromWordID
// по слову найти его ID - быстрая проверки уже имеющихся слов
var WordIdFormWord = make(map[string]int)
/*
// для обеспечения уникальности узлов:
type WordUnicum struct {
	ID int
	word string
}
var WordUnicumIdStr=make(map[WordUnicum]int)// для каждого сочетания  выдается ID узла
*/

// счктчик при создании узлов
var lastIDwordTree=0

// подошла очередь инициализации
func afterLoadTempArr() {
	loadWordTree()
	/*
	   //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	   	SetNewWordTreeNode("и") // а я нехочу такое дело
	   //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	   	SaveWordTree()
	*/
	//SetNewWordTreeNode("привет")
	//str:=GetWordFromWordID(144)
	//if len(str)>0{}
	//	updateWordTreeFromTempArr(2,2)
	afterLoadPhraseArr()
	// приветсвую 634 привет 556   ветвеление - прикалываюсь 483
	//	DeleteWord(483)
}

// создать первый, символьный уровень дерева
func initWordTree(vt *WordTree) {
	for i := 0; i < len(symbolsArr); i++ {
		createNewNodeWordTree(vt,0,symbolsArr[i])
		SymboIDfromSymbl[symbolsArr[i]] = i
		sr := []rune(symbolsArr[i])
		symbolsRuneArr[i] = sr[0]
		SymboIDfromRune[sr[0]] = i
		if symbolsArr[i] == "п"{
			symbolsArr[i] = "п"
		}
	}
	// updateWordTreeFromID()
	return
}

// создать новый узел дерева слов
func createNewNodeWordTree(parent *WordTree,id int,word string) *WordTree {
	if parent == nil { return nil	}
	if word == "" { return nil }
	// после удаления слова - запрет на вставку новых слов до перезагрузки
	if blockingNewInsertWordAfterDeleted {
		lib.WritePultConsol("ПОСЛЕ УДАЛЕНИЯ СЛОВА - ЗАПРЕТ НА ВСТАВКУ НОВЫХ СЛОВ ДО ПЕРЕЗАГРУЗКИ")
		return nil
	}

	// notAllowScanInThisTime=true // запрет показа карты при обновлении
	if id == 0 {
		lastIDwordTree++
		id = lastIDwordTree
	} else {
		if lastIDwordTree < id {
			lastIDwordTree = id
		}
	}
	/* лишнее
	// не позволять дублировать узлы
		unic:=WordUnicumIdStr[WordUnicum{id,word}]
		if unic!=0 {// уже есть такой узел
			old:=WordTreeFromID[unic] // WordTreeFromID еще актуальный для всех уже созданных, раз не создано нового
			return old //nil
		}
	*/
	var newW WordTree
	newW.ID = id
	newW.ParentID = parent.ID
	newW.ParentNode = parent
	newW.Symbol = word

	parent.Children = append(parent.Children, newW)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newT *WordTree // называть переменные зарезервированным словом new - моветон
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == newW.ID {
			newT = &parent.Children[i]
		}
	}
	// WordTreeFromID[new.ID]=new
	// т.к. append меняет длину массива, перетусовывая адреса, то нужно:
	updateWordTreeFromID(parent)// здесь потому, что при загрузке из файла нужно на лету получать адреса
	// notAllowScanInThisTime=false  НЕТ, иначе оно прерывает более общий запрет и возникают гонки
	return newT
}

// корректируем адреса всех узлов
func updateWordTreeFromID(parent *WordTree) {
	updatingWordTreeFromID(parent)
}

// проход всего дерева
func updatingWordTreeFromID(wt *WordTree) {
	if wt.ID > 0 {
		wt.ParentNode = WordTreeFromID[wt.ParentID] // wt.ParentNode адрес меняется из=за corretsParent(,
		WordTreeFromID[wt.ID] = wt
		WordTreeFromStr[wt.Symbol] = append(WordTreeFromStr[wt.Symbol], wt)
		//WordUnicumIdStr[WordUnicum{wt.ParentID,wt.Symbol}] = wt.ID
	}
	if wt.Children == nil {	return } // конец ветки
	for i := 0; i < len(wt.Children); i++ {
		updatingWordTreeFromID(&wt.Children[i])
	}
}

// загрузить дерево слов
func loadWordTree() {
	initWordTree(&VernikeWordTree)
	strArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/word_tree.txt")
	cunt := len(strArr)
	// просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) < 2 {
			panic("Сбой загрузки дерева слов: ["+strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p := strings.Split(strArr[n], "|#|")
		word := p[1]
		idP := strings.Split(p[0], "|")
		id, _ := strconv.Atoi(idP[0])
		parentID, _ := strconv.Atoi(idP[1])
		// новый узел с каждой строкой из файла
		createNewNodeWordTree(WordTreeFromID[parentID], id, word)
	}
	return
}

// сохранить дерево слов
func SaveWordTree() {
	var out = ""
	// записываем, начиная со второго уровня (уровень символов не пишем)
	cnt := len(VernikeWordTree.Children)
	for n := 0; n < cnt; n++ {
		out += getWtreeNode(&VernikeWordTree.Children[n])
	}
	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_reflex/word_tree.txt", out)
	return
}

// получить ветку дерева слов в виде строки
func getWtreeNode(wt *WordTree) string {
	var out = ""
	if wt.ParentID > 0 {
		out += strconv.Itoa(wt.ID) + "|"
		out += strconv.Itoa(wt.ParentID) + "|#|"
		out += wt.Symbol + "\r\n"
	}
	if wt.Children == nil {	return out } // конец
	for n := 0; n < len(wt.Children); n++ {
		out += getWtreeNode(&wt.Children[n])
	}
	return out
}

/* вставить новое слово в дерево:
 найти подходящий узел и если еще нет - вставить новый.

noCreate true - не создавать новых узлов при распознавании в рефлексах и т.п.
 */
func SetNewWordTreeNode(word string,noCreate bool) int {
	if len(word) == 0 { return 0 }
	notAllowScanInThisTime = true // запрет показа карты при обновлении

	// распознаем что можем
	if len(word) > 0 {
		WordDetection(word,noCreate)
		// Запись недостающего в дерево слов происходит в WordDetection
	}
	notAllowScanInThisTime = false
	return DetectedUnicumID
}

/* создание ветки символов, начиная с заданного узла
Рекурсивно проходит посимвольно, дабавляя в дерево слов
 */
func createWordTreeNodes(word []rune,wt *WordTree) int {
	if len(word) == 0 { // закончить проход
		if wt==nil{
			return 0
		}
		return wt.ID
	}
// создать узел дерева слов
	node := createNewNodeWordTree(wt, 0, string(word[0]))

	// рекурсивно добавить ost к узлу node
	ost := word[1:]
	id := createWordTreeNodes(ost, node)

	return id
}
____________________________________________

BOT/brain/words_sensor/word_tree_pult.go
____________________________________________
/* выдать дерево слов на Пульт по GET-запросу http://go:8181?get_word_tree=1
Запросы даются при запуске и при изменении размера файла дерева /memory_reflex/word_tree.txt
 */

package word_sensor

import (
	"sort"
	"strconv"
)

func initWordPult(){
	//str:=GetPhraseTreeForPult()
	//if len(str)>0{}
}

// образ дерева фраз для вывода на Пульт
var wordTreeModel = ""

// проход дерева фраз
func GetWordTreeForPult() string {
	if notAllowScanInThisTime { return "!!!" }
	wordTreeModel = ""
	scanWordNodes(-1, &VernikeWordTree)
	return wordTreeModel
}

// сканировать узел дерева слов
func scanWordNodes(level int, wt *WordTree) {
	if wt.ID > 0 {
		wordTreeModel += setWordShift(level)
		wordTreeModel += wt.Symbol + "(" + strconv.Itoa(wt.ID) + ")<br>\r\n"
	}
	level++
	for n := 0; n < len(wt.Children); n++ {
		scanWordNodes(level,&wt.Children[n])
	}
}

// отступ
func setWordShift(level int) string {
	var sh = ""
	for n := 0; n < level; n++ {
		sh += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	}
	return sh
}

// выдать на пульт список слов в алфавитном порядке
func GetWordsListForPult() string {
	if len(WordIdFormWord) == 0 {
		return "Еще не готовы данные, обновите чуть позже."
	}

	wArr := make([]string, 0, len(WordIdFormWord))
	for w, _ := range WordIdFormWord {
		wArr = append(wArr, w)
	}
	sort.Strings(wArr)
	var out = "<table class='main_table'  cellpadding=0 cellspacing=0 border=1 width='100%' style='font-size:14px;'>"
	out += "<tr><td class='table_header'>Слово</td>"
	out += "<td class='table_header' width=100>ID</td>"
	out += "<td class='table_header'>Слово</td>"
	out += "<td class='table_header' width=100>ID</td>"
	out += "<td class='table_header'>Слово</td>"
	out += "<td class='table_header' width=100>ID</td></tr><tr>"
	var col = 0
	for n := 0; n < len(wArr); n++ {
		if len(wArr[n]) == 0 { continue	}
		if col >= 3 {
			out += "</tr><tr>"
			col = 0
		}
		id := strconv.Itoa(WordIdFormWord[wArr[n]])
		out += "<td class='table_cell'>" + wArr[n] + "</td>"
		out += "<td class='table_cell'>" + id + "<img src='/img/delete.gif' class='select_control' onClick='delete_word(" + id + ")'></td>"
		col++
	}
	count := strconv.Itoa(len(wArr))
	out += "</tr></table><b>Всего: " + count + " слов</b>"
return out
}
____________________________________________

BOT/brain/words_sensor/words_sensor.go
____________________________________________
/* сенсор символов, слов и фраз */
package word_sensor

import (
	"BOT/lib"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

/* вспомогательный шаблон воспринятых слов с числом повторений восприятия
Индекс - фраза, [0] = число повторнеий [1] - сколько раз уже сохраняется в списке

Фомат words_temp_arr.txt: Число повторнеий|#|Фраза|#|число сохранений (старость)

Число сохранений - в func UpdateWordTreeFromTempArr при пересохранении страых слов, фраз списков
 */
var tempArr=make(map[string][]int)
var MapGwardWordTempArr=lib.RegNewMapGuard()
///////////////////////////////////////

func init(){
	// залить сохранненный tempArr
	//!!!loadTempArr()
	afterLoadTempArr()
	//GetExistsPraseID("дурак")
}

// сохранить шаблон воспринятых слов
func SaveTempArr() {
	var str[]string
	/* Вы не поверите, но при переборе tempArr вдруг очищается tempArr!?
	for k, v := range tempArr {
		str = append(str, k + "|#|" + strconv.Itoa(v[0]) + "|#|" + strconv.Itoa(v[1]))
	}
	А вот если разделить действи как ниже, то очищается, но нормально записывает в str. Жуть...
	*/
	lib.MapCheckBlock(MapGwardWordTempArr)
	for s, v := range tempArr {
		val0:=v[0]
		val1:=v[1]
		fileS:=s + "|#|" + strconv.Itoa(val0) + "|#|" + strconv.Itoa(val1)
		str = append(str,fileS)
	}
	lib.MapFree(MapGwardWordTempArr)

	sort.Strings(str)
	var out = ""
	for i := 0; i < len(str); i++ {
		p:=strings.Split(str[i], "|#|")
		out+=p[1]
		out+="|#|" + p[0]
		if len(p)>2{// новый член формата
			out+="|#|" + p[2]
		}else{
			out+="|#|0"
		}
//		out+= strconv.Itoa(int(gomeostas.LifeTime/100))+ "|#|"
		out+= "\r\n"
	}

	lib.WriteFileContent(lib.GetMainPathExeFile() + "/memory_reflex/words_temp_arr.txt", out)
}

// загрузить шаблон воспринятых слов
func loadTempArr() {
	lib.MapCheckWrite(MapGwardWordTempArr)
	tempArr = make(map[string][]int)
	lib.MapFree(MapGwardWordTempArr)
	wArr, _ := lib.ReadLines(lib.GetMainPathExeFile() + "/memory_reflex/words_temp_arr.txt")
	for n := 0; n < len(wArr); n++ {
		if len(wArr[n]) < 4 { continue }
		p := strings.Split(wArr[n], "|#|")
		v, _ := strconv.Atoi(p[0])
		c:=0
		if len(p)>2 { // новый член формата
c, _ = strconv.Atoi(p[2])
		}
		lib.MapCheckWrite(MapGwardWordTempArr)
		tempArr[p[1]] = []int{v,c}
		lib.MapFree(MapGwardWordTempArr)
	}
}

// Новая порция текста для формирования дерева слов
// Эта функция работает при накачке текстов из main.go
func SetNewTextBlock(txt string) string {
	/*
	txt, err := url.QueryUnescape(txt)
	if err != nil {
		log.Fatal(err)
		return "ОШИБКА раскодировки"
	}
 */
	txt = strings.Replace(txt, "{#1}", "%", -1)
	// txt= strings.Replace(txt, "{#2}", "", -1)// кавычки просто очищены (пусть будет афазия :)
	var res = ""
	// разделяем на фразы
	strArr := strings.Split(txt, "|#") // а не |#| - чтобы оставлять разделитель "|"
	for i := 0; i < len(strArr); i++ {
		addNewtempArr(strArr[i] + "|")
	}
	UpdateWordTreeFromTempArr(4,6)

	return res
}

/* добавляются как целиком фраза, так и все слова во фразе
Тут же дозаполняетс дерево слов уже многократно провторяющимися элементами.

Вернуть разпознанное ID фразы и саму фразу (режим диалога с Пультом, а не режим накачки текстами)
Если фраза не распознана, то возвращается 0 и фраза с нераспознанными словами.

При превышении числа накопленных слов в func UpdateWordTreeFromTempArr
происходит их распознавание и удаление из /memory_reflex/words_temp_arr.txt

Здесь str - с разделителями '|' (привет| |о| |тупой)
 */
func addNewtempArr(str string)(int,string) {
	loadTempArr()// вский раз получать обновленный массив накоплений слов

	//не добавлять в words_temp_arr.txt, если такая фраза (str) есть
	// убрать разделители '|'
	str0 := strings.Replace(str, "|", "", -1)
	// если есть такая фраза в Дереве, то выдать ее ID - ТОЛЬКО РАСПОЗНАВАНИЕ, А НЕ СОЗДАНИЕ НОВОЙ
	phrID:=GetExistsPraseID(str0)
	if phrID>0{
		CurrentPhrasesIDarr = append(CurrentPhrasesIDarr, DetectedUnicumPhraseID)
		// если вся фраза известа, то ничего больше не делать
		//out_str = append(out_str, out)

		CurretWordsIDarr=GetWordsIDarrFromPraseNodeID(phrID)

		return phrID,str0
		//noAddToList=true
	}
	// из-за непонятного глюка (внезапная очистка) с непосредственным добавлением ввожу промежуточный буфер:
	var tArr []string // новые фразы и слова, которые нужно добавить в tempArr (words_temp_arr.txt)

	if len(str) < 2 { return phrID,str }
	var out_str string // массив распознанных слов во фразе - для вывода на пульт
	// разделить слова
	// разбить по разделителям
	sArr := strings.Split(str, "|")
	var wordCount = 0// число только слов, с пробелами
	var onlyWordCount=0// число только слов, без пробелов
	// фраза без разделителей и отдельные слова (более 1 символа) - в шаблон:
	var out = ""
	//var isBegin = true
	// подсчет числа слов
	cnt := len(sArr)
	for i := 0; i < cnt; i++ {
		if len(sArr[i]) == 0 {
			continue
		}
		if sArr[i]!=" "{
			onlyWordCount++
		}
		wordCount++
		// во фразе оставляем и слова и символы, т.е. восстанавливаем фразу как она была
		out += sArr[i]
	}

	if onlyWordCount > 1 && onlyWordCount < 6 { // записать всю фразу, но не длинее 6 слов
		tArr = append(tArr, out)
	}
	///////////////   конец для всей фразы

	// Отдельные слова.
	zp := regexp.MustCompile(`[ ,:.+\(\)]`)
	sPath := zp.Split(out, -1)// Выделить слова по пробелам и знакам препинания
		//sArr = strings.Split(sPath, " ")
	for i := 0; i < len(sPath); i++ {
		wID,ok:=WordIdFormWord[sPath[i]]
		if ok{// слово уже известно
			out_str += " " + sPath[i]
			CurretWordsIDarr=append(CurretWordsIDarr,wID)
			continue
		}
		r := []rune(sPath[i])
		if len(r) < 2 {
			continue
		}
		tArr = append(tArr, sPath[i])
		CurretWordsIDarr=append(CurretWordsIDarr,-1)
		out_str += " <span style='color:red' title='НЕ РАСПОЗНАННО'>" + sPath[i] + "</span> "
	}

	// добавляем новое
	for i := 0; i < len(tArr); i++ {
		nc := 0
		lib.MapCheck(MapGwardWordTempArr)
		_,ok:=tempArr[tArr[i]]// var tempArr=make(map[string][]int)
		if ok {
			lib.MapCheck(MapGwardWordTempArr)
			nc = tempArr[tArr[i]][0]
		}
		lib.MapCheckWrite(MapGwardWordTempArr)
		tempArr[tArr[i]] = []int{nc + 1, 0}
		lib.MapFree(MapGwardWordTempArr)
	}

	SaveTempArr()

	return phrID,out_str
}
____________________________________________

BOT/lib/any.go
____________________________________________
/* разные вспомогательные функции
 */

package lib

import (
	"math"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

func init() {
	/*
		a0Arr:=[]int{2,1,3}
		aArr:=[]int{3,4,2,2,5,3,2,1,3}
		len0F:=len(a0Arr)
		lenF:=len(aArr)
		// есть ли в предшествующем фрагменте cycle.funcArr повторение cycle.func0Arr
		if lenF > 2*len0F {
			// предшествующй фрагмент cycle.funcArr
			fArr:=aArr[:(lenF-len0F)]
			prevF:=fArr[(len(fArr)-len0F):]
			if(EqualArrs(prevF, a0Arr)){
				return
			}
		}
		return
	*/
}

// удалить повторяющиеся значения
func RemoveDuplicateValues(intSlice []int) []int {
	keys := make(map[int]bool)
	list := []int{}

	// If the key(values of the slice) is not equal
	// to the already present value in new slice (list)
	// then we append it. else we jump on another element.
	for _, entry := range intSlice {
		if _, value := keys[entry]; !value {
			keys[entry] = true
			list = append(list, entry)
		}
	}
	return list
}

// удалить повторяющиеся значения строк
/*
func RemoveDuplicateStringValues(intSlice []string) []string {
	keys := make(map[int]bool)
	list := []string{}

	// If the key(values of the slice) is not equal
	// to the already present value in new slice (list)
	// then we append it. else we jump on another element.
	for _, entry := range intSlice {
		if _, value := keys[entry]; !value {
			keys[entry] = true
			list = append(list, entry)
		}
	}
	return list
}
*/

// удалить из массива значение с индексом ID, сохранив порядок
func RemoveArrIndex(intSlice []int, id int) []int {
	if len(intSlice)-1 == id {
		intSlice = intSlice[:len(intSlice)-1]
	} else {
		copy(intSlice[id:], intSlice[id+1:])
	}
	return intSlice
}

// случайное из диапазона чисел
func RandChooseDiapazonQuest(first int, last int) int {
	rand.Seed(time.Now().UnixNano())
	n := first + rand.Intn(last-first+1)
	return n
}

// случайное из нескольких чисел
func RandChooseQuest(nums ...int) int {
	var count = 0
	var num []int
	for _, n := range nums {
		num = append(num, n)
		count++
	}
	rand.Seed(time.Now().UnixNano())
	n := rand.Intn(count + 1)
	return num[n]
}

// случайное из массива
func RandChooseIntArr(n []int) int {
	var count = 0
	var num []int

	if n == nil {
		return 0
	}
	for _, n := range n {
		num = append(num, n)
		count++
	}
	rand.Seed(time.Now().UnixNano())
	v := rand.Intn(count)
	return num[v]
}

// сравнение массивовв int на идентичность
func EqualArrs(a, b []int) bool {
	if len(a) != len(b) {
		return false
	}
	for i, v := range a {
		if v != b[i] {
			return false
		}
	}
	return true
}

// вернуть наиболее повторяющееся по числу значение массива и среди них первое из равных
func GetMaxCountVal(arr []int) int {
	// группируем значения в a
	var a = make(map[int]int)
	for i := 0; i < len(arr); i++ {
		a[arr[i]]++
	}
	// находим максимальное или первое из равных
	var max = 0
	var ind = 0
	for k, v := range a {
		if max < v {
			max = v
			ind = k
		}
	}
	return ind
}

// индекс значения в массиве
func IndexValInArr(arr []int, val int) (bool, int) {
	if arr == nil {
		return false, 0
	}
	for k, n := range arr {
		if n == val {
			return true, k
		}
	}
	return false, 0
}

// есть такое значение в массиве
func ExistsValInArr(arr []int, val int) bool {
	if arr == nil {
		return false
	}
	for _, n := range arr {
		if n == val {
			return true
		}
	}
	return false
}

// есть такое значение в массиве строк
func ExistsValInStringArr(arr []string, val string) bool {
	if arr == nil {
		return false
	}
	for _, n := range arr {
		if n == val {
			return true
		}
	}
	return false
}

// есть такое значение в массиве с учетом его сортировки
func ExistsValInArrSort(arr []int, val int) bool {
	if arr == nil {
		return false
	}
	for i := 0; i < len(arr); i++ {
		if arr[i] == val {
			return true
		}
	}
	return false
}

// есть полное строковое значение в строке значений
func ExistsValStrInList(List string, val string, razd string) bool {
	var sArr []string

	sArr = strings.Split(List, razd)
	for _, n := range sArr {
		if n == val {
			return true
		}
	}
	return false
}

func Abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

// имеют ли два числа одинаковые знаки.
func EqualSigns(num1, num2 int) bool {
	return (num1 >= 0 && num2 >= 0) || (num1 < 0 && num2 < 0)
}

// числа имеют разные знаки (одно положительное, другое отрицательное)
func IsDiffersOfSign(n1 int, n2 int) bool {
	if (n1 > 0 && n2 < 0) || (n1 < 0 && n2 > 0) {
		return true
	}
	return false
}

func AbsFloate(x float64) float64 {
	if x < 0 {
		return -x
	}
	return x
}

func Max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

func Min(x, y int) int {
	if x > y {
		return y
	}
	return x
}

// сохранить массив int в переменной
func SaveArrToVar(arr []int, to []int) []int {
	for a := 0; a < len(arr); a++ {
		to = append(to, arr[a])
	}
	return to
}

// текстовый массив в числовой
func IntArrToStrArr(sArr []string) []int {
	var out []int
	var id int

	for i := 0; i < len(sArr); i++ {
		id, _ = strconv.Atoi(sArr[i])
		if id != 0 {
			out = append(out, id)
		}
	}

	return out
}

// числовой массив в текстовый
func StrArrToIntArr(sArr []int) []string {
	var out []string
	var id string

	for i := 0; i < len(sArr); i++ {
		id = strconv.Itoa(sArr[i])
		out = append(out, id)
	}

	return out
}

// объединить 2 массива в один
func SumArr(arr1 []int, arr2 []int) []int {
	var out = append(arr1, arr2...)
	return UniqueArr(out)
}

// убрать дублеры в массиве
func UniqueArr(intSlice []int) []int {
	keys := make(map[int]bool)
	list := []int{}
	for _, entry := range intSlice {
		if _, value := keys[entry]; !value {
			keys[entry] = true
			list = append(list, entry)
		}
	}
	return list
}

// убрать дублеры в массиве 2D
func UniqueArr2D(intSlice [][]int) [][]int {
	var list [][]int
	listBuf := [][]int{{0}}

	for _, arr := range intSlice {
		isFind := true
		for _, arr1 := range listBuf {
			if EqualArrs(arr, arr1) == true {
				isFind = false
				break
			}
		}
		if isFind {
			list = append(list, arr)
			listBuf = append(listBuf, arr)
		}
	}
	return list
}

/*
	найти какие значения m1 есть в m2

т.е. m2 должен быть частью m1 или полностью совпадать с ним
*/
func GetExistsIntArs(m1 []int, m2 []int) []int {
	var found []int
	for i := 0; i < len(m1); i++ {
		for j := 0; j < len(m2); j++ {
			if m1[i] == m2[j] {
				found = append(found, m1[i])
			}
		}
	}
	return found
}

/*
	найти каких значений m1 нет в m2

т.е. m2 должен быть частью m1 или полностью совпадать с ним
*/
func GetDifferentIntArs(m1 []int, m2 []int) []int {
	var diff []int
	var exists = false
	for i := 0; i < len(m1); i++ {
		exists = false
		for j := 0; j < len(m2); j++ {
			if m1[i] == m2[j] {
				exists = true
				break
			}
			if !exists {
				diff = append(diff, m1[i])
			}
		}
	}
	return diff
}

// ////////////////////////////////////////
//
//	возвращает ближайшее целочисленное значение типа float64.
func RoundToFloate(x float64) float64 {
	t := math.Trunc(x)
	if math.Abs(x-t) >= 0.5 {
		return t + math.Copysign(1, x)
	}
	return t
}

// возвращает ближайшее целочисленное значение типа int.
func RoundToInt(x float64) int {
	t := math.Trunc(x)
	if math.Abs(x-t) >= 0.5 {
		return int(t + math.Copysign(1, x))
	}
	return int(t)
}

//////////////////////////////////////////

// Найти в массиве int индексы всех членов по значению
func FindIndexes(arr []int, value int) []int {
	indexes := []int{}
	for i, num := range arr {
		if num == value {
			indexes = append(indexes, i)
		}
	}
	return indexes
}

/*
	не выдавать уже имеющиеся цифры в arr, т.е. вернет только уникальный ряд (сгруппирует)

Из idArr := []int{2, 25, 26, 2, 25, 26, 3, 5, 7, 2, 25, 26, 3, 3, 3, 12, 12, 2, 25, 26}
вернет: 1,25,26,2,5,6,12
*/
func RemoveDuplicates(arr []int) []int {
	result := []int{}
	for i := 0; i < len(arr); i++ {
		if ExistsValInArr(result, arr[i]) {
			continue
		}
		result = append(result, arr[i])
	}
	return result
}

____________________________________________

BOT/lib/files.go
____________________________________________
/*
файловые функции

ReadLines
ReadIntArr
ReadFloate64Arr
WriteNewString
RewriteFileContent
WriteFileContent
ReadFileContent
*/

package lib

import (
	"bufio"
	"fmt"
	_ "fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
)

// путь к исполняемому файлу
var MainPathExeFile string

func GetMainPathExeFile() string {
	//	mainPathExeFile=os.Args[0] - путь с самим файлом
	ex, err := os.Executable()
	if err != nil {
		panic(err)
	}
	//	mainPathExeFile, _ = filepath.EvalSymlinks(ex)  - путь с самим файлом
	MainPathExeFile = filepath.Dir(ex)
	return MainPathExeFile
}

// размер файла
func GetFileSize(path string)(int, bool){
	file, err := os.Open(path)
	if err != nil {
		panic(err)
		return 0, false
	}
	defer file.Close()
	fileInfo, err := file.Stat()
	if err != nil {
		panic(err)
		return 0, false
	}
	fileSize := fileInfo.Size()
	return int(fileSize),true
}

// read line by line into memory
// all file contents is stores in lines[]
func ReadLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	/*  нельзя, т.к. тогда будут срабатывать for n := 0; n < len(lines); n++ {
	if lines == nil{// всегда позволять считывать хотя бы пустое lines[0]
		lines = append(lines, "")
	}
	*/
	return lines, scanner.Err()
}

func ReadIntArr(path string) ([]int, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []int
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		val, _ := strconv.Atoi(scanner.Text())
		lines = append(lines, val)
	}
	return lines, scanner.Err()
}

func ReadFloate64Arr(path string) ([]float64, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []float64
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		val, _ := strconv.ParseFloat(scanner.Text(), 64)
		lines = append(lines, val)
	}
	return lines, scanner.Err()
}

func WriteNewString(file string, str string) {
	f, _ := os.OpenFile(file, os.O_APPEND, 0666)
	f.WriteString(str + "\n")
	//	fmt.Println("Запись: ", err);
	defer f.Close()
}

// перезаписать файл  (должен быть файл)
func RewriteFileContent(file string, content string) {
	f, _ := os.OpenFile(file, os.O_RDWR, 0666)
	f.WriteString(content)
	//	fmt.Println("Запись: ", err);
	defer f.Close()
}

// записать файл, если нет - создать
func WriteFileContent(file string, content string) {
	if len(content) == 0 {
		return
	}
	//	f, _ := os.OpenFile(file,os.O_CREATE, 0666)
	f, _ := os.Create(file)
	f.WriteString(content)
	//	fmt.Println("Запись: ", err);
	defer f.Close()
}
// записать даже если content пустой
func WriteFileContentExactly(file string, content string) {
	f, _ := os.Create(file)
	f.WriteString(content)
	//	fmt.Println("Запись: ", err);
	defer f.Close()
}

// считывание файла в строку
func ReadFileContent(file string) string {
	//f, _ := os.Open(file)
	data, _ := ioutil.ReadFile(file)
	//defer f.Close()
	return string(data)
}

// копировать файл
func CopyFile(sourceFile string, destinationFile string) {
	input, err := ioutil.ReadFile(sourceFile)
	if err != nil {
		fmt.Println(err)
		return
	}

	err = ioutil.WriteFile(destinationFile, input, 0644)
	if err != nil {
		fmt.Println("Error creating", destinationFile)
		fmt.Println(err)
		return
	}
}

// удалить все файлы из папки
func ClinerAllFromDir(dir string){
//	dir := "/files/"

	d, err := os.Open(dir)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer d.Close()

	files, err := d.Readdir(-1)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, file := range files {
		err = os.Remove(dir + file.Name())
		if err != nil {
			fmt.Println(err)
		} else {
			//fmt.Println("Deleted file:", file.Name())
		}
	}
}
____________________________________________

BOT/lib/map_analog.go
____________________________________________
/* иммитация map на основе обычных массивов для работы с массивами типа nodeFromID
Паники типа "concurrent map writes" уже не будет.
Гонки же лоступа оказываются не опасными и повлияют на стабильность.
Смысл в том, что при записи проверяется существование индекса и если его нет, массив наращивается недостающими членами.

Для кажого массива задается:
var AFromID []*aNode  // сам массив
//var AFromID = make([]*aNode, len(strArr))//задать сразу имеющиеся в файле число при загрузке
// запись члена
func WriteAFromID(index int, value *aNode) {
	addAFromID(index)
	AFromID[index] = value
}
func addAFromID(index int){
	if index >= len(AFromID) {
		newSlice := make([]*aNode, index+1)
		copy(newSlice, AFromID)
		AFromID = newSlice
	}
}
// считывание члена
func ReadeAFromID(index int) (*aNode,bool){
	if index >= len(AFromID) || AFromID[index]==nil {
		return nil,false
	}
	return AFromID[index],true
}
Шаблон использования:
var AFromID []*anode
AFromID = make([]*anode, len(str))//задать сразу имеющиеся в файле число при загрузке из файла

WriteAFromID(index, value)
addAFromID(index)
node,ok:=ReadeAFromID(id)
if !ok {return }
В случае передобра range НУЖНО ПРОВЕРЯТЬ if v==nil{continue}:
for _, v := range{
if v==nil{continue}
////////////////////////////////////////////

В случае массива массивов (например, var importanceConditinArr [][]*importance)
перед операией append()
// если нет еще индекса ProblemID, то нарастить
	addImportanceConditinArr(ProblemID)
	importanceConditinArr[ProblemID]=append(importanceConditinArr[ProblemID],&node)
///////////////////////////

Можно спосокйно использовать for id, v := range AutomatizmFromId {
и там просто проверять if v!=nil для массивов, возвращающих указатель

	for k, v := range AutomatizmFromId {
		if v==nil{
			continue
		}
}

Не следует забивать большее число членов, чем их реально т.к. вознкнет проблема опрделения числа реальных членов.

 */

package lib

//////////////////////////  ТЕСТИРОВАНИЕ
/*
func init() {
	MapExperiment() // задержка всегда 10 сек !
	return
}
var yyyArr=make([]*aNode,100)
func MapExperiment(){
	fmt.Println("ПРОВЕРКА")
	WriteAFromID(1,&aNode{1,333})
	go www()
	go rrr()

	time.Sleep(10 * time.Second)// подождем когда все горутины пройдут

	return
}
//////////////////////////////////////
func rrr(){
	for i := 0; i < 10000; i++ {
		for k, _ := range yyyArr {
			_,ok:=ReadeAFromID(k)
			if 	ok{
			}
		}
	}

	for i := 0; i < 10000; i++ {
		_,ok:=ReadeAFromID(i)
		if 	ok{

		}
	}
	return
}
func www(){
	for i := 0; i < 10000; i++ {
		//WriteAFromID(i,&aNode{1,i})
	}
}

////////////////////////////////////////////////

type aNode struct {
ID int
val int
}

var AFromID []*aNode
//var AFromID = make([]*aNode, 20000)//задать сразу имеющиеся в файле число
func WriteAFromID(index int, value *aNode) {
	if index >= len(AFromID) {
		// Увеличиваем размер среза до нужного индекса
		newSlice := make([]*aNode, index+1)
		// Копируем значения из старого среза в новый
		copy(newSlice, AFromID)
		AFromID = newSlice
	}
	// Записываем значение в ячейку с указанным индексом
	AFromID[index] = value
}
func ReadeAFromID(index int) (*aNode,bool){
	if index >= len(AFromID) {
		return nil,false
	}
	return AFromID[index],true
}
*/
/////////////////////////////////////////////





///////// GPT предложило
/*
type MyStruct struct {
	Value int
}

type DynamicArray struct {
	data map[int]*MyStruct
}

// NewDynamicArray creates and initializes a new DynamicArray
func NewDynamicArray() *DynamicArray {
	return &DynamicArray{
		data: make(map[int]*MyStruct),
	}
}

// AddElement adds an element at a specified index
func (da *DynamicArray) AddElement(index int, element *MyStruct) {
	da.data[index] = element
}

// GetElement retrieves an element at a specified index
func (da *DynamicArray) GetElement(index int) (*MyStruct, error) {
	if val, ok := da.data[index]; ok {
		return val, nil
	}
	return nil, fmt.Errorf("index %d not found", index)
}

// RemoveElement removes an element at a specified index
func (da *DynamicArray) RemoveElement(index int) error {
	if _, ok := da.data[index]; ok {
		delete(da.data, index)
		return nil
	}
	return fmt.Errorf("index %d not found", index)
}

func main() {
	myArray := NewDynamicArray()

	myArray.AddElement(100, &MyStruct{Value: 10})
	myArray.AddElement(200, &MyStruct{Value: 20})
	myArray.AddElement(300, &MyStruct{Value: 30})

	fmt.Println("Map after adding elements:", myArray.data)

	element, err := myArray.GetElement(200)
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Element at index 200:", element)
	}

	err = myArray.RemoveElement(200)
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Map after removing element at index 200:", myArray.

*/
____________________________________________

BOT/lib/map_guard.go
____________________________________________
/*
Способ защиты карт от ПРОБЛЕМЫ С КАРТАМИ "concurrent map writes"
ПРИМЕР реализации предотвращения конфликтов карты
_________________________________________________________
Штатный способ через мьютексы - для любых ситуаций:

import "sync"

var mapArr = make(map[int]int)
var MutexMapArr = &sync.RWMutex{}

// при записи:
MutexMapArr.Lock()
mapArr[key] = value
MutexMapArr.Unlock()

// при чтении или перед перебором for:
MutexMapArr.RLock()
value,ok := mapArr[key]
if ok{ ... }
MutexMapArr.RUnlock()
Но концепция мьютексом очень капризна и бывают ситуации гонок без какого-либо оповещения,
просто функции перестают работать из-за гонок: дедлоком (взаимной блокировкой) между RLock() и Lock().
Примекр такой ситуации описан в комменте в самом низу пакета.
Хотя есть встроенный инструмент для обнаружения состояний гонки, называемый "Race Detector" он не спасает положения.

Из-за такой непродуманности для нашего случая использования только одной главной горутины и запросов GET и POST,
которые и приводят к конкурентности, лучше использовать следующий способ.
Его доддержка остается в этом пакете всегда активной.


Самодельный способ, применимый в случае отсуствия конкурирующих горутин (одновременность только за счет POST и GET обработки)
// при записи - ждем когда отпустит и снова блокируем
lib.MapCheckBlock(mapGwardYyyArr)
yyyArr[1]=i
lib.MapFree(mapGwardYyyArr)

// при одиночном чтении - просто ждем когда отпустит
lib.MapCheck(mapGwardYyyArr)
_,ok:=yyyArr[1]

// при чтении или перед перебором - ждем когда отпустит и снова блокируем
lib.MapCheckBlock(mapGwardYyyArr)
for k, _ := range yyyArr {
	if k > 0 {	}
}
lib.MapFree(mapGwardYyyArr)
______________________________________________________


 */
package lib

import (
	"math/rand"
	"time"
)

//////////////////////////////////////////////////

/*
// ПРОВЕРКА РАБОТЫ ВАРИАНТА С МЬЮТЕКСАМИ:
// тестовый вызов из brain/init.go RunInitialisation(): lib.MapExperiment()
func MapExperiment(){
	fmt.Println("ПРОВЕРКА")
	yyyArr[1]=333
	go www()
	go rrr()

	time.Sleep(3 * time.Second)

	return
}
//////////////////////////////////////
func rrr(){
	mutexYyyArr.Lock()
	for i := 0; i < 10000; i++ {
		for k, _ := range yyyArr {
			if k > 0 {
			}
		}
	}
	mutexYyyArr.Unlock()

	for i := 0; i < 10000; i++ {
		_,ok:=readYyyArr(1)
		if 	ok{

		}

		//readYyyArr(1)

	}
	return
}
func www(){

	for i := 0; i < 10000; i++ {
		writeYyyArr(1,i)
	}
}


/////////////////////////////////////
var yyyArr=make(map[int]int)
var mutexYyyArr = &sync.RWMutex{}
func writeYyyArr(key, value int) {
	mutexYyyArr.Lock()
	yyyArr[key] = value
	mutexYyyArr.Unlock()
}

func readYyyArr(key int) (int,bool) {
	mutexYyyArr.RLock()
	value,ok := yyyArr[key]
	mutexYyyArr.RUnlock()
	return value,ok
}
*/




/*
//////////////////////////////////////////////////////////
// ПРОВЕРКА РАБОТЫ ВАРИАНТА С выжидательной блокировкой:
// тестовый вызов из func init()

var yyyArr=make(map[int]int)
var mapGwardYyyArr=RegNewMapGuard()

func MapExperiment(){
	fmt.Println("ПРОВЕРКА")
	yyyArr[1]=333
	go www()
	go rrr()

	time.Sleep(10 * time.Second)// подождем когда все горутины пройдут

	return
}
//////////////////////////////////////
func rrr(){
	MapCheckBlock(mapGwardYyyArr)
	for i := 0; i < 10000; i++ {
		for k, _ := range yyyArr {
			if k > 0 {
			}
		}
	}
	MapFree(mapGwardYyyArr)

	for i := 0; i < 10000; i++ {
		MapCheck(mapGwardYyyArr)
		_,ok:=yyyArr[1]
		if 	ok{

		}
	}
	return
}
func www(){
	MapCheckWrite(mapGwardYyyArr)
	for i := 0; i < 10000; i++ {
		yyyArr[1]=i
	}
	MapFree(mapGwardYyyArr)
}
*/


/////////////////// ПОДДЕРЖКА  MapFlag["map1Arr"]=
/* массив флагов блокировки для всех карт
Если MapFlag[nn]==true, то не может быть записи или считывания.
Это - коунтер блокировок.

var mapGwardActionsImageArr=lib.RegNewMapGuard()

MapCheckWrite(mapGwardActionsImageArr) - блокировка при записи
lib.MapCheckBlock(mapGwardActionsImageArr) - блокировка при чтении больших фрагментов
lib.MapFree(mapGwardActionsImageArr) - отпускаем на один шажок

lib.MapCheck(mapGwardActionsImageArr)  - просто ждем когда отпустит

lib.MapCheck(mapGwardActionsImageArr)
val,ok:=yyyArr[nn]
if ! ok{
return nil
}
///////////////////////////////////////////////////
 */

var MapFlag []int

var mapGuardCounter=0
func RegNewMapGuard()int{
	if mapGuardCounter==0{
		MapFlag=append(MapFlag,0)
	}
	mapGuardCounter++
	MapFlag=append(MapFlag,0)
	MapFlag[mapGuardCounter]=0
	return mapGuardCounter
}

func init(){
	rand.Seed(time.Now().UnixNano())

// MapExperiment() // Для тестирования нужно раскомментировать "ПРОВЕРКА РАБОТЫ ВАРИАНТА С выжидательной блокировкой"
}
///////////////////////////////////

func MapCheck(index int){
	/* В очень сложных, вложенных конструкциях возникают дублирования блокировок и код подвешивается.
	С помощью TodoPanic("В коде есть место, вызывающее постоянную блокировку!")
	можно находить такие места и разруливать. Но это не всегда возможно,
	поэтому вполне приемлемо будет просто сбрасывать такой цикл: MapFlag[index]=0;break

	if n>1000{ должно быть больше, чем время на запись в карту между MapCheckWrite() и MapFree()!!!
	 */
	n:=0
	for MapFlag[index]>0{
		if n>1000{
// TodoPanic("В коде есть место, вызывающее постоянную блокировку!")
MapFlag[index]=0;break // освобождение после каких-то лаж в коде...
		}
		n++
		time.Sleep(10 * time.Microsecond)
	}
}
// блокировка при записи с удалением одновременности
func MapCheckWrite(index int){
	/* нарушить одновременность вызова MapCheck с данным индексом
		Вероятность одновременного вызова, превышающего скорость отработки флага, ничтожна,
		но механизмы провоцируют строгую одновременность. Поэтому такую одновременность нарушим,
		выжидая случайное число микросекунд (от 0 до 100).
		Такое время при записи является приемлемым для задач проекта Beast.
	После этого какой-то из вызовов MapCheckBlock(index) окажется первым и закроет последующий MapCheck(index).
	*/
	MapCheck(index)
	if true {// false - чтобы отключить лекарство от одновременности вызова MapCheckWrite
		randNum := rand.Intn(10000)
//time.Sleep(time.Duration(randNum) * time.Nanosecond) очень нестабильна, зависит от текущей нагрузки
// задержка - просто выполнением числа операций
		for randNum>0{
			randNum--
		}
		MapCheck(index)
	}
	MapFlag[index]++
}
// блокировка при переборе типа чтении for k, _ := range yyyArr или больших фрагментов кода
func MapCheckBlock(index int){
	MapCheck(index)
	MapFlag[index]++
}

func MapFree(index int){
	MapFlag[index]--
	if MapFlag[index]<0{
		MapFlag[index]=0
	}
}
///////////////////////////////////////////////////////////////
/*  ИСПОЛЬЗОВАНИЕ:
// при записи - ждем когда отпустит и снова блокируем
lib.MapCheckBlock(mapGwardYyyArr)
yyyArr[1]=i
lib.MapFree(mapGwardYyyArr)

// при одиночном чтении - просто ждем когда отпустит
lib.MapCheck(mapGwardYyyArr)
_,ok:=yyyArr[1]

// при чтении или перед перебором - ждем когда отпустит и снова блокируем
lib.MapCheckBlock(mapGwardYyyArr)
for k, _ := range yyyArr {
	if k > 0 {	}
}
lib.MapFree(mapGwardYyyArr)
*/
//////////////////////////////////////



/* Как можно выловить лажу мьютекса:
в строке MutexTempArr.Lock() правок кнопкой по Lock выйти на ее реализацию в пакете mutext.go
там поставить точку прерывания на runtime_SemacquireMutex:
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false, 0)
	}
и тестировать разные случаи пока не будет останов в точке, после чего прояти по стеку и найти откула пошел конфликт.

Пример сбоя в ГО при использовании мьютексов для карты cyclesArr:
func endDereamsCycles(){
	MutexCyclesArr.RLock()
	for id, v := range cyclesArr {
		if v.dreaming>0 {
			endBaseIdCycle(id)
		}
	}
	MutexCyclesArr.RUnlock()
}
но в функции endBaseIdCycle(id) уже используется мьютекс:
func endBaseIdCycle(baseID int){
	foundMainCycle(baseID)//найти новый главный цикл по значимости, кроме удаляемого
	MutexCyclesArr.Lock()
	delete(cyclesArr, baseID)
	MutexCyclesArr.Unlock()
}
Возникает гонка, останавливающая выполнение функции в
func (rw *RWMutex) Lock() {
...
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false, 0)
	}
}
Как правильно использовать мьютексты в таких случаях?


В данном случае проблема связана с дедлоком (взаимной блокировкой) между RLock() и Lock(). Вы пытаетесь получить мьютекс на запись в то время как у вас уже есть чтения блокировка этого же мьютекса. Итог - взаимная блокировка.

Метод RLock() не предназначен для "повышения" на блокировку записи. Если вы захватили блокировку чтения с помощью RLock(), вы не сможете получить блокировку записи с помощью Lock(), пока не освободите блокировку чтения с помощью RUnlock().

Вам нужно будет пересмотреть свою стратегию использования мьютекса. Вы можете:

Использовать блокировку записи Lock() в endDereamsCycles(), а не блокировку чтения. Это значит, что во время выполнения endDereamsCycles() другие горутины не смогут читать или писать в cyclesArr.

Разделить функцию endDereamsCycles() на две части: одна для чтения и фильтрации значение из cyclesArr, и другая для вызова endBaseIdCycle(). Это позволит вам освободить блокировку чтения перед вызовом endBaseIdCycle().

Сделать копию данных (cyclesArr), которые будут обрабатываться, защитив их с помощью блокировки чтения, а затем производить изменения вне блокированного блока.

Пожалуйста, имейте в виду, что правильное решение зависит от контекста вашего приложения и того, какие гарантии вы хотите предоставить.

Возможно, лучшим вариантом для данного случая будет подход, при котором вы создаете копию keys, которые нужно обработать. Затем обрабатываете каждый key / value в отдельности.

Код ниже показывает пример такого подхода:


import "sync"

var cyclesArr= make(map[int]*MyStruct) // предположим, что MyStruct - ваш произвольный тип
var MutexCyclesArr = &sync.RWMutex{}

func endDereamsCycles(){
    // захватываем блокировку чтения
    MutexCyclesArr.RLock()

    // создаем копию ключей
    keys := make([]int, 0, len(cyclesArr))
    for id, v := range cyclesArr {
        if v.dreaming > 0 {
            keys = append(keys, id)
        }
    }
    // освобождаем блокировку чтения
    MutexCyclesArr.RUnlock()

    // теперь обрабатываем наши элементы
    for _, id := range keys {
        endBaseIdCycle(id)
    }
}

// найти новый главный цикл по значимости, кроме удаляемого - предположительные операции
func foundMainCycle(baseID int){
  //добавьте здесь ваши операции
}

func endBaseIdCycle(baseID int) {
    foundMainCycle(baseID)
    MutexCyclesArr.Lock()
    delete(cyclesArr, baseID)
    MutexCyclesArr.Unlock()
}
В этом коде, на первом этапе функции endDereamsCycles() мы захватываем блокировку только для чтения и копирования ключей нуждающихся в обработке элементов.

Затем, после того как все ключи были скопированы, мы освобождаем блокировку чтения и переходим к обработке каждого ключа в отдельности с захватом блокировки записи в endBaseIdCycle().


Go имеет встроенный инструмент для обнаружения состояний гонки, называемый "Race Detector". Этот инструмент может автоматически обнаруживать состояния гонки во время выполнения и обычно очень полезен для обнаружения сложных ошибок конкурентной среды.

Использование детектора состояний гонок ("Race Detector") в Go легко. Вам просто нужно добавить флаг -race при запуске вашей программы или тестов.

Для запуска все своей программы с детектором состояний гонок, вы можете использовать команду go run -race myprogram.go.

Для запуска тестов с использованием детектора состояний гонок, вы можете использовать команду go test -race mypackage.

В обоих случаях детектор состояний гонок автоматически обнаруживает и сообщает о любых встреченных условиях гонки.

Однако, важно отметить, что детектор состояний гонок не может найти потенциальные состояния гонок, которые могут произойти в будущем. Он обнаруживает только те состояния гонок, которые фактически произошли во время выполнения.

Также стоит отметить, что, несмотря на то что "Race Detector" - это полезный  инструмент, его использование увеличивает время выполнения программы и требует больше памяти, поэтому его обычно не используют в продакшен-среде.

В IDE GoLand, вы можете включить "Race Detector" несколькими способами:

Настройка среды выполнения для конкретной задачи:

Откройте настройки "Run/Debug Configurations".
Выберите конкретное задание на выполнение (или создайте новое).
В разделе "Go build flags" напишите -race.
Сохраните изменения и запустите задание на выполнение.
Непосредственно перед запуском кода:

Откройте терминал GoLand.
Используйте go run -race или go test -race followed by your .go file or package.
В обоих случаях GoLand запустит ваш код с "Race detector", и если будут обнаружены состояния гонки, они будут отображены в консоли вывода.

 */
____________________________________________

BOT/lib/pult.go
____________________________________________
/* Передача инфы на Пульт */

package lib

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// строка вывода на пульт - для func WritePultConsol
var WritePultConsolStr = ""

/*
	вывести на консоль Пульта

Сообщения накапливаются в WritePultConsolStr и откправлются с каждым пульсом
*/
func WritePultConsol(print string) {
	WritePultConsolStr = print + "<br>" + WritePultConsolStr
	// очищать от форматирования тегами, проверка - в func RunInitialisation()
	reg := regexp.MustCompile(`<\/?[^>]+>`)
	print = reg.ReplaceAllString(print, " ")
	fmt.Println("НА ПУЛЬТ: ", print)
}

// функция вызова паники с информированием в логе Пульта
func TodoPanic(panicWarning string) {
	WritePultConsol("<span style='color:red;font-size:19px;font-weight:bold;'>ПАНИКА: </span> " + panicWarning)
	panic(panicWarning)
}

//////////////////////////////////////////////////

// В main.go действия БЛОКИРУЮТСЯ В РЕЖИМЕ СНА: !sleep.IsSleeping
var ActionsForPultStr = ""     // Строка действий для Пульта
var BlockingAnyActions = false // блокировать выдачу на пульт любых действий во время сна или dreaming
/*
	вывести на Пульт действия Бота строкой lib.SentActionsForPult("xcvxvxcv")

Каждая акция - в формате: вид действия (1 - действие рефлекса, 2 - фраза) затем строка акции,
например: "1|Предлогает поиграть" или "2|Привет!"
Можно передавать неограниченную последовательность акций, разделяя их "||"
например: "1|Предлогает поиграть||2|Привет!"

За один пульс может набраться очередь нескольких действий, например,
после действия может измениться условие и будет новое действие.
Поэтому их все нужно выводить, но не допускать дублирования.

Дубли так же контролируются в doublesRemove - проверкой строки "энергичность",
т.к. могут быть несколько одинаковых сообщений с разной энергичностью.
*/
func SentActionsForPult(act string) {
	if BlockingAnyActions {
		ActionsForPultStr = ""
		return
	}
	if len(ActionsForPultStr) > 0 { // еще не прочитана предыдущая инфа т.к. читается раз в пульс, а после действия может измениться условие и будет новое действие
		// посмотреть дубли в очереди
		sArr := strings.Split(ActionsForPultStr, "||")
		for _, v := range sArr {
			if act == v || len(v) < 4 { // уже есть такое действие, не добавлять в очередь
				return
			}
		}
		ActionsForPultStr += "||" + act // добавить новую к предыдущему
		return
	}
	ActionsForPultStr = act
}

/*
Дубли контролируются проверкой строки "энергичность",
т.к. могут быть несколько одинаковых сообщений с разной энергичностью.
Для одинаковых действий ставится максимальная Энергичность из имеющихся.

Вызывается в main.go перед отправкой на Пульт.
*/
func DoublesRemove(str string) string {
	if str == "" {
		return ""
	}

	out := ""
	var s1Arr = make(map[int]string)
	var efArr = make(map[int]int)
	var s2Arr = make(map[int]string)
	sArr := strings.Split(str, "||")
	// собираем инфу
	for i := 0; i < len(sArr); i++ {
		s1, ef, s2 := cutEnerg(sArr[i])
		s1Arr[i] = s1
		efArr[i] = ef
		s2Arr[i] = s2
	}
	// ищем дубли
	var dArr []int
	for i := 0; i < len(sArr); i++ {
		if ExistsValInArr(dArr, i) {
			continue
		} // дубли не смотреть
		var max = efArr[i]
		out += s1Arr[i] + " Энергичность:"
		for j := 0; j < len(sArr); j++ {
			if i == j {
				continue
			} // саму себя не сравнивать
			if s1Arr[i] == s1Arr[j] {
				if efArr[j] > max {
					max = efArr[j]
				}
				dArr = append(dArr, j) // не смотреть это
				continue
			}
		}

		out += strconv.Itoa(max) + "</b>" + s2Arr[i]
		out += "||"
	}

	return out
}

// рубим сообщение на части
func cutEnerg(str string) (string, int, string) {
	if str == "" {
		return "", 0, ""
	}
	s1 := strings.Split(str, "Энергичность:")
	out1 := s1[0]
	if len(s1) == 1 {
		return out1, 0, ""
	}
	s2 := strings.Split(s1[1], ")</b>")
	out2, _ := strconv.Atoi(strings.Split(s2[0], "=")[1])
	out3 := s2[1]
	return out1, out2, out3
}

//////////////////////////////////////////////////

/*
	Показать непонимания, растерянность -

в случае отсуствия пси-реакций но не Лени.
lib.SentConfusion()
*/
func SentConfusion(detaile string) {
	ActionsForPultStr = "10|" + detaile
}

/*
можно ли показывать акции рефлексов с автоматизмами в одной плашке действия Beast на пульте
ТЕПЕРЬ: НИКОГДА НЕЛЬЗЯ показывать акции рефлексов с автоматизмами в одной плашке!
РАНБШЕ БЫЛО:
Eсли noReflexWithAutomatizm==true и если в ActionsForPultStr есть автоматизм, то строки рефлексов удаляются.
При запуске автоматизма из цикла мышления (на 3-м уровне осмысления) устанавливается NoReflexWithAutomatizm =true
Применение:
ActionsForPultStr=SharedRflexWithAutomatizm(true)
*/

var NoReflexWithAutomatizm = false

func SharedReflexWithAutomatizm() string {
	if !NoReflexWithAutomatizm {
		//		return ActionsForPultStr  НИКОГДА НЕЛЬЗЯ показывать акции рефлексов с автоматизмами в одной плашке!
	}
	out := ""
	sArr := strings.Split(ActionsForPultStr, "||")
	n := 0
	for i := 0; i < len(sArr); i++ {
		id, _ := strconv.Atoi(sArr[i][:1]) // первый символ - идентификатор типа акции
		if id > 1 || sArr[i][:2] == "10" { // нужно еще учесть активацию infoFunc(13)
			if n > 0 {
				out += "||"
			}
			out += sArr[i]
			n++
		}
	}
	return out
}

____________________________________________

BOT/lib/str.go
____________________________________________
/*
строковые функции: сравнение слов как в мозгах - эта версия не используется...

распознание слова:
Первая и последняя буквы должны совпадать
Буквы внутри могут быть вперемешку, но должно максимально совпадать количество букв внутри
1 - точное соотвествие
2 - одна буква не совпадает
3 - две буквы не совпадают
4 - более 2 не совпадает
*/

package lib

import (
	_ "fmt"
)

/* сравнение слов
str1 - слово мз введенной фразы, сравнивается со словами из файловой матрицы matrix_utf.txt
*/
func Wcomp(str1 string, s1Count int, str2 string) int {
	// s1:="привет"; 	s2:="привет"; 	if s1==s2{return 222}else{return 111}
	// if s1[4:5] == s2[4:5]{return 222}else{return 111}
	if str1 == str2 { // точное соотвествие
	return 1
	} else { // неточное
		//	s1_count:=len(str1)
		s2Count := len(str2)
		//	return int8(s1_count);

		//если число букв str1 меньше 4-х, то - только точное сравнение
		if s1Count < 8 { return 0}

		// число символов не равно
		if s1Count != s2Count { return 0 }

		// не выделить первую и последнюю в 1 и 2 символьном слове и нет точного соотвествия (ранее)
		if s1Count <4 { return 0 }

		// первые буквы и последние буквы равны
		if str1[:2] != str2[:2] || str1[s1Count - 2:] != str2[s2Count - 2:] { // первая и/или последняя не совпадают
			return 0
		} else { // первая и последняя совпадают,нужно оченить промежуточные
			// return 111
			var found int
			found= 0
			for i := 1; i < s1Count/2-1; i++ {
				for j := 1; j < s2Count/2-1; j++ {
					if str1[2 * i - 2:2 * i] == str2[2 * j - 2:2 * j] {
						found++
					}
				}
			}
			// если число найденных во втором слове не совпадает с числом букв первого, то - 2
			if (s1Count / 2 - 2) != found {
				return 0
			}
			// если число найденных во втором слове совпадает с числом букв первого, то - 2
			return 2
		}
		//return 0
	}
	//return 0
}

/* сравненеие двух строк в виде rune
0 - строки равны, 1 - первая длинее, 2 - вторая длинее, 3 - строки не равны при одмнаковой длине, 4 - не равня с самого начала
*/
func RuneCompare(r1 []rune, r2 []rune) int {
	len1 := len(r1)
	len2 := len(r2)
	if len1 > len2 { return 1	}
	if len2 > len1 { return 2	}
	for i := 0; i < len1; i++ {
		if r1[i] != r2[i] { return 3 }
	}
	return 0
}
____________________________________________

BOT/lib/tree.go
____________________________________________
/* Пример кода для пддержки деревьев
типа с фиксированным числом уровней - наиболее физиологичный тип.

!!! Для деревьев с переменной длиной веток, как WordTree или PhraseTree нужно адаптировать функции
т.к. там в структурах узлов - значение только одного текущего уровня. Зато там максимально компактная запись в файл.

Для использования кода просто "sample" везде менять на название вида дерева.

Формат записи примера дерева sampleTree в файл:
ID|ParentNode|levVal1|levVal2|levVal3|levVal4

Для тестирования раскомментируй tryExperiment() в func init() !
*/

package lib

import (
	"strconv"
	"strings"
	//"BOT/lib"
)

///////////////////////////////////////////////

func init() {

	// tryExperiment() //  проверка работы дерева в разных ситуациях

	return
}

///////////////////////////////////////////

type sampleNode struct { // узел дерева
	ID int
	/* в ветке узелы получают все значения предыдущих, кроме пока еще не достигших даноого уровня.
	   т.е. если в каждом узле сохраняется инфа о всех значениях узлов предыдущих уровней вентки.
	*/
	levVal1 int // значение узла первого уровня (всегда ID образа)
	levVal2 int // значение узла второго уровня (всегда ID образа)
	levVal3 int // значение узла третьего уровня (всегда ID образа)
	levVal4 int // значение узла четвертого уровня (всегда ID образа)

	Children   []sampleNode // дочерние узлы (ветвление) НЕ АДРЕСА, А РЕАЛЬНЫЕ ОБЪЕКТЫ
	ParentID   int          // ID родителя
	ParentNode *sampleNode  // адрес родителя
}

var sampleTree sampleNode

var sampleTreeFromID = make(map[int]*sampleNode)

// /// предотвращение ПАНИКИ КАРТ типа "concurrent map writes"
var sampleTreeMapFlag = false //true- в карту идет запись
var MapGwardSampleTreeFromID = RegNewMapGuard()

///////////////////////////////////////

/////////////////////////////////////////// функции поддержки
/* Для создания нового узла ветки дерева
Возвращает ID узла и указатель на него - в любом случае, создан ли новый узел или найден такой же уже имеющийся.
*/
var lastIDNodeTree = 0 // счетчик ID узлов, хранит ID хранит ID последнего созданного узла
func createNodeTree(parent *sampleNode, id int, levVal1 int, levVal2 int, levVal3 int, levVal4 int, checkUnicum bool) (int, *sampleNode) {
	if parent == nil {
		return 0, nil
	}

	// если есть такой узел, то не создавать новый
	if checkUnicum {
		idOld, nodeOld := checkSampleBranchFromCondition(levVal1, levVal2, levVal3, levVal4)
		if idOld > 0 {
			return idOld, nodeOld
		}
	}

	if id == 0 {
		lastIDNodeTree++
		id = lastIDNodeTree
	} else {
		//		newW.ID=id
		if lastIDNodeTree < id {
			lastIDNodeTree = id
		}
	}

	var node sampleNode
	node.ID = id
	node.ParentNode = parent
	node.ParentID = parent.ID
	node.levVal1 = levVal1
	node.levVal2 = levVal2
	node.levVal3 = levVal3
	node.levVal4 = levVal4

	parent.Children = append(parent.Children, node)
	// четко находим новый вставленный член (а не &parent.Children[count-1])
	var newN *sampleNode
	for i := 0; i < len(parent.Children); i++ {
		if parent.Children[i].ID == node.ID {
			newN = &parent.Children[i]
		}
	}

	sampleTreeMapFlag = true
	sampleTreeFromID[node.ID] = &node
	sampleTreeMapFlag = false

	// т.к. append меняет длину массива, перетусовывая адреса, то нужно обновить адреса в AutomatizmTreeFromID:
	// ЭТО НУЖНО ДЕЛАТЬ ТОЛЬКО ДЛЯ ДЕРЕВЬЕВ!
	updatingSampleTreeFromID(parent) // здесь потому, что при загрузке из файла нужно на лету получать адреса

	return id, newN
}

// ////////////////////////////////
func checkSampleBranchFromCondition(levVal1 int, levVal2 int, levVal3 int, levVal4 int) (int, *sampleNode) {
	cond := []int{levVal1, levVal2, levVal3, levVal4}
	maxLev := 4
	/*
		maxLev:=0// максимальный уровень в условии cond
		for i := 0; i < len(cond); i++ {
			if cond[i]==0{
				break
			}
			maxLev++
		}*/
	id, maxLevel := findSampleBrange(0, cond, &sampleTree)
	if id > 0 {
		if maxLevel == maxLev { // есть такая ветка, просто возвращаем ее значения
			MapCheckBlock(MapGwardSampleTreeFromID)
			node, ok := sampleTreeFromID[id]
			if ok {
				return id, node
			} else { // такого не должно быть
				TodoPanic("В func FindSampleTreeNodeFromCondition должно быть значение карты sampleTreeFromID.") //вызвать панику
			}
			MapFree(MapGwardSampleTreeFromID)
		} else { // нужно дорастить ветку
			return 0, nil
		}
	} else { // ID==0 вообще нет совпадений, нужно наращивать с основы
		return 0, nil
	}

	return 0, nil
}

// ////////
// рекурсивно корректируем адреса всех узлов
func updatingSampleTreeFromID(rt *sampleNode) {
	if rt.ID > 0 {
		MapCheckBlock(MapGwardSampleTreeFromID)
		rt.ParentNode = sampleTreeFromID[rt.ParentID] // wr.ParentNode адрес меняется из=за corretsParent(,
		MapFree(MapGwardSampleTreeFromID)
		sampleTreeFromID[rt.ID] = rt
	}
	if rt.Children == nil { // конец ветки
		return
	}
	for i := 0; i < len(rt.Children); i++ {
		updatingSampleTreeFromID(&rt.Children[i])
	}
}

///////////////////////////////////////////////////////////

// проверка работы дерева в разных ситуациях
func tryExperiment() {

	// ветки дерева для тестиварования
	fileTree := []string{
		"1|0|1|0|0|0",
		"2|0|2|0|0|0",
		"3|0|3|0|0|0",
		// второй уровень
		"4|1|1|10|0|0",
		"5|1|1|11|0|0",
		// третий уровень
		"6|5|1|11|2|0",
		// четвертый уровень
		"7|6|1|11|2|3",
	}
	// загрузить дерево из данных вверху
	initSampleTree(fileTree)

	//cond:= []int{9,2,3,4, } // вообще нет такой ветки ни на одном уровне  (maxLevel==0)
	//cond:= []int{1,4,3,4, } // есть совпадение только для первого уровня (maxLevel==1)
	cond := []int{1, 11, 5, 8} // не хватает 3 и 4-го уровня (maxLevel==2)
	//cond:= []int{1,11,2,8, } // не хватает 4-го уровня  (maxLevel==3)
	//cond:= []int{1,11,2,3, } // существующая ветка дерева (maxLevel==4)

	//cond:= []int{1,11,0,0, } // условие поиска не до конца ветки - нулевые ID образов 3 и 4 уровней НЕ ДОЛЖНО БЫТЬ ТАКОГО

	ID, maxLevel := findSampleBrange(0, cond, &sampleTree)
	if ID > 0 {
		if maxLevel == len(cond) { // найден конечный узел ветки

			// что-то сделать при нормальном распознавании
			return
		} else { // нужно дорастить ветку
			//Eсли условие cond []int содержит значение 0 для какого-то уровня, то на этом уровне поиск заканчивается.
			// считается нормальный поиск по неполному условию, доращивать нет смысла
			if cond[maxLevel] > 0 {
				ID = addFromNodeIDsToBrange(ID, maxLevel, cond)
				return
			}
		}
	} else { // ID==0 вообще нет совпадений, нужно наращивать с основы
		ID = addFromNodeIDsToBrange(sampleTree.ID, maxLevel, cond)
		return
	}

	return
}

//////////////////////////////////////////////////////////////

/*
	поиск конечного узла ветки (lastBrangeID) дерева (root - ID начального узла == 0) по массиву ID узлов ветки (cond []int)

Поиск начинается с первого узла (по значению cond[0]).
Если первый узел найден, то findSampleBrange вызывается рекурсивно для поиска следующего узла
и так далее, пока не будет найден удел последнего члена cond.
Если для текущего cond[level] узел в редеве не существует, то в дереве доращивается ветка по значениям cond []int.

Возвращает ID последней найденной ветки и ее уровень, если конечный узел не найден (если найден, то len(cond)-1).
Если вернула уровень меньше, чем максимальное число уровней ветки,
то ветка доращивается func addNodesToBrange
*/
func findSampleBrange(level int, cond []int, root *sampleNode) (int, int) {

	// Обработка случая когда мы достигли конца дерева
	if cond == nil || len(cond) <= 0 || level >= len(cond) {
		return root.ID, len(cond)
	}

	// Поиск узла с ID из списка cond в дочерних узлах текущего узла
	for _, child := range root.Children {
		if isEquivalentCondition(level, &child, cond) {
			// Если узел найден, продолжим рекурсивно искать в нем
			id, lev := findSampleBrange(level+1, cond, &child)
			if id == 0 {
				/*Eсли условие cond []int содержит значение 0 для какого-то уровня, то на этом уровне поиск заканчивается.
				if cond[lev]==0{// считается нормальный поиск по неполному условию

				}*/
				//return child.ID,lev
				return 0, lev // не найдено совпадение на данном уровне
			}
			// сюда доходит только после полного прохода всех итераций, так что просто возвращаем достигнутое
			return id, lev
		}
	}

	// не найден узел на данном уровне
	return 0, level
}

// //
func isEquivalentCondition(level int, node *sampleNode, cond []int) bool {
	// массив значений по уровням - в зависимости от числа уровней в ветке, прописывается вручную
	nArr := []int{node.levVal1, node.levVal2, node.levVal3, node.levVal4}

	for i := 0; i < len(cond) && i <= level; i++ {
		if cond[i] != nArr[i] {
			return false
		}
	}
	return true
}

/////////////////////////////////////////////////////////////////////////

/*
	Доращивание ветки, начиная с заданного узла fromID по массиву всех значений для ветки

Возвращает ID конечного узла ветки.
*/
func addFromNodeIDsToBrange(fromID int, lastLevel int, condArr []int) int {

	MapCheckBlock(MapGwardSampleTreeFromID)
	node, ok := sampleTreeFromID[fromID]
	MapFree(MapGwardSampleTreeFromID)
	if !ok {
		return 0
	}

	lastNodeID, _ := addNodesToBrange(node, lastLevel, condArr)

	return lastNodeID
}

/*
	рекурсивно создать все недостающие уровни ветки по значениям condArr []int

func addNodesToBrange запускается ТОЛЬКО ЕСЛИ точно известно, что узла с condArr [0] у fromNode нет !!!
Проверки на существование здесь нет: createNodeTree(fromNode, ..., false)
*/
func addNodesToBrange(fromNode *sampleNode, level int, condArr []int) (int, *sampleNode) {
	if fromNode == nil {
		return 0, nil
	}
	if level >= len(condArr) {
		return fromNode.ID, fromNode
	}
	//vArr := make([]int, len(condArr))// все vArr[n] имеют нулевые значения
	vArr := []int{0, 0, 0, 0}
	for i := 0; i <= level; i++ { // заполнить vArr до текущего уровня включительно, остальные - оставить нулями
		vArr[i] = condArr[i]
	}
	// с каждым уровнем vArr[n] добавляется новое значение для создания узла ветки следующего уровня.
	//true потому, что новый узел нужно делать только если такого еще нет!!!
	_, node := createNodeTree(fromNode, 0, vArr[0], vArr[1], vArr[2], vArr[3], true)
	level++
	id, node := addNodesToBrange(node, level, condArr)
	return id, node
}

///////////////////////////////////////

/* загрузить записанное дерево
 */
func loadSampleTree() {
	//	strArr,_:=lib.ReadLines(lib.GetMainPathExeFile()+"/memory_psy/automatizm_tree.txt")
	//	initSampleTree(strArr)
}
func initSampleTree(strArr []string) {
	sampleTreeMapFlag = true
	sampleTreeFromID = make(map[int]*sampleNode)
	sampleTreeMapFlag = false
	// определить нулевой узел
	sampleTreeFromID[0] = &sampleTree // все по нулям по умолчанию

	cunt := len(strArr)
	//просто проход по всем строкам файла подряд так что сначала идут дочки, потом - их родители
	for n := 0; n < cunt; n++ {
		if len(strArr[n]) == 0 {
			continue
		}
		if len(strArr[n]) < 2 {
			panic("Сбой загрузки дерева: [" + strconv.Itoa(n) + "] " + strArr[n])
			return
		}
		p := strings.Split(strArr[n], "|")
		id, _ := strconv.Atoi(p[0])
		parentID, _ := strconv.Atoi(p[1])

		levVal1, _ := strconv.Atoi(p[2])
		levVal2, _ := strconv.Atoi(p[3])
		levVal3, _ := strconv.Atoi(p[4])
		levVal4, _ := strconv.Atoi(p[5])

		// новый узел с каждой строкой из файла
		MapCheckBlock(MapGwardSampleTreeFromID)
		parent, ok := sampleTreeFromID[parentID]
		MapFree(MapGwardSampleTreeFromID)
		if ok {
			// false - не проверять наличие ветки т.к. еще нет веток, они только глузятся
			createNodeTree(parent, id, levVal1, levVal2, levVal3, levVal4, false)
		}
	}
	return
}

// ///////////////////////////////////
func SaveSampleTree() {
	var out = ""
	cnt := len(sampleTree.Children)
	for n := 0; n < cnt; n++ { // чтобы записывалось по порядку родителей
		out += getSampleNode(&sampleTree.Children[n])
	}
	//lib.WriteFileContent(lib.GetMainPathExeFile()+"/memory_psy/automatizm_tree.txt",out)
	return
}
func getSampleNode(wt *sampleNode) string {
	var out = ""
	//	if wt.ParentID>0 {
	out += strconv.Itoa(wt.ID) + "|"
	out += strconv.Itoa(wt.levVal1) + "|"
	out += strconv.Itoa(wt.levVal2) + "|"
	out += strconv.Itoa(wt.levVal3) + "|"
	out += strconv.Itoa(wt.levVal4)
	out += "\r\n"
	//	}
	if wt.Children == nil { // конец
		return out
	}
	for n := 0; n < len(wt.Children); n++ {
		out += getSampleNode(&wt.Children[n])
	}
	return out
}

/////////////////////////////////////

///////    попытка челез интерфесы и reflect оазывается слишком накрученной...
/*  из-за статической типизации в Go, создание новых узлов динамически в общем случае будет очень сложно реализовать,
особенно если структура sampleNode имеет разное количество уровней nodeN int.

type nodeI interface {
	CreateBranch(cond []int) nodeI
	// Остальные методы Node, которые можно определить
}
func findSampleBrange(level int, cond []int, root interface{}) int {

	if root == nil || level >= len(cond) {
		return 0
	}

	val := reflect.ValueOf(root)
	typeOfS := val.Type()


	// Достаем ID узла из структуры и проверяем соответствие с числом из cond
	for i := 0; i < val.NumField(); i++ {
		if typeOfS.Field(i).Name == "ID" && val.Field(i).Interface().(int) == cond[level] {
			// Увеличиваем уровень
			level++
			if level >= len(cond) {
				return val.Field(i).Interface().(int)
				// Когда достигаете конца текущей ветви:
				//newNode := nodeI.CreateBranch(cond[level:])
				//return findSampleBrange(level+1, cond, newNode)
			}
		}
	}

	// Если ветка поиска по cond закончилась, ищем далее по объекту root
	for i := 0; i < val.NumField(); i++ {
		if typeOfS.Field(i).Name == "Children" {
			children := val.Field(i)
			for j := 0; j < children.Len(); j++ {
				result := findSampleBrange(level, cond, children.Index(j).Interface())
				if result > 0 {
					return result
				}
			}
		}
	}

	return 0
}
*/
//////////////////////////////////////////////////

____________________________________________

BOT/main.go
____________________________________________
/*
индексная страница http://localhost:8181/index

В проекте много глобальных переменных, что привычно раздражает Свидетелей инкапсуляции и непорочного пространства имен,
но ТАК НУЖНО (спорно) для одганизации среды,
схожей с организацией линкующих указателей в мозге (т.е. связей с одного распознавателя к целому ансаблю - объекту).
Ну и есть немало других вещей, нарушающих Порядок и Традиции Golang.
Попытки использовать горутины оказались просто неуместными (спорно) и просто ненужными, учитывая вряд ли в чем-то могущий быть выигрыш.
Короче, код предоставляется на вольное растерзание и свободное экспериментирование, без претензий, сорри за возможный негатив.
Везде много пространных комментариев, которые запутывают даже меня, но они НУЖНЫ.
*/

package main

import (
	"BOT/brain"
	"BOT/brain/action_sensor"
	"BOT/brain/gomeostas"
	"BOT/brain/psychic"
	"BOT/brain/reflexes"
	"BOT/brain/sleep"
	"BOT/brain/transfer"
	"BOT/brain/update"
	"BOT/brain/words_sensor"
	"BOT/lib"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

var xxxxxxx = 0 // в дебаге иногда начинается циклический вызов, это - защелка

// true - остановка всей активности для совершения критических глобальных операций
var isGlobalStopAllActivnost = false

var noСloseWeght = false

func receiveSend(resp http.ResponseWriter, r *http.Request) {
	resp.Header().Set("Access-Control-Allow-Origin", "*")
	resp.Header().Set("Access-Control-Allow-Headers", "Content-Type")

	// посылается постоянно раз в 1сек (из /common/linking.php) или с запросом или для подтверждения связи,
	// а так же для передачи по текущему пульсу информации от Beast, например WritePultConsol()
	if r.Method == "POST" {
		if gomeostas.CheckBeastDeath() {
			isGlobalStopAllActivnost = true
			_, _ = fmt.Fprint(resp, "!!!")
		}

		if !isGlobalStopAllActivnost {
			// текстовый блок для набивки дерева слов-фраз из http://go/pages/words.php
			text_block := r.FormValue("text_block")
			if len(text_block) > 0 {
				brain.IsPultActivnost = true
				res := word_sensor.SetNewTextBlock(text_block)
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, res)
				return
			}
			// текст из окна ввода с пульта
			text_dlg := r.FormValue("text_dlg")
			if len(text_dlg) > 0 {
				brain.IsPultActivnost = true
				is_input_rejim, _ := strconv.Atoi(r.FormValue("is_input_rejim")) // режим быстрого формирования у-рефлексов
				if is_input_rejim == 0 {                                         // наоборот
					reflexes.IsUnlimitedMode = 1
				} else {
					reflexes.IsUnlimitedMode = 0
				}
				toneID, _ := strconv.Atoi(r.FormValue("pult_tone"))
				pultMood := r.FormValue("pult_mood") // тон сообщения
				moodID, _ := strconv.Atoi(pultMood)  // настроение сообщения
				res := word_sensor.VerbalDetection(text_dlg, is_input_rejim, toneID, moodID)
				if moodID > 0 {
					// учесть мотивирующий эффект
					action_sensor.UpdateMoodEffectFromMessage(moodID)
				}
				// если добавлены пусковые стимулы (нажаты кнопки на пульте)
				set_img_action := r.FormValue("set_img_action")
				action_sensor.IsPultAction()
				if len(set_img_action) > 0 {
					// brain.IsPultActivnost = true

					enegry, _ := strconv.Atoi(r.FormValue("food_portion"))
					action_sensor.SetActionFromPult(set_img_action, enegry)
					/*
						// активировать дерево действием
						reflexes.ActiveFromAction()
						brain.IsPultActivnost = false
					*/
				}

				reflexes.ActiveFromPhrase() // активировать дерево рефлексов фразой - только для условных рефлексов
				brain.IsPultActivnost = false
				// и если есть ответ, то тут же выдать на Пульт
				var answerStr = ""
				if len(lib.ActionsForPultStr) > 5 && !sleep.IsSleeping {
					lib.ActionsForPultStr = lib.SharedReflexWithAutomatizm()
					if len(lib.ActionsForPultStr) > 5 {
						if false { // проверка акций
							arts := strings.Split(lib.ActionsForPultStr, "||")
							for i := 0; i < len(arts); i++ {
								art := strings.Split(arts[i], "|")
								if len(art) != 2 {
									return
								}
							}
						}
					}
					//могут быть несколько одинаковых сообщений с разной энергичностью. Для одинаковых действий ставится максимальная Энергичность из имеющихся.
					answerStr = lib.DoublesRemove(lib.ActionsForPultStr)

					lib.ActionsForPultStr = "" // очистка для новой порции
				}
				_, _ = fmt.Fprint(resp, res+"|&|"+answerStr)
				return
			}

			// отправить на пульт состояние гомеостаза Beast и его базовые контексты
			sincronism := r.FormValue("sincronism")
			if len(sincronism) > 0 {
				// выполнить цикл действий по пульсу перед отправкой результата на Пульт
				brain.SincroTic()
				_, _ = fmt.Fprint(resp, "sincronism")
				return
			}

			// отправить на пульт состояние гомеостаза Beast и его базовые контексты
			getParams := r.FormValue("get_params")
			if len(getParams) > 0 {
				brain.IsPultActivnost = true
				outStr := gomeostas.GetCurGomeoParams()

				var waitingPeriod = "_"
				// был Стиму оператора, но 2 пульса как нет ответного автоматизма
				if psychic.NoautomatizmAfterStimul == 2 {
					// активация дерева не вызвала автоматизм и не было периода ожидания
					waitingPeriod = "zzz"
					/*	psychic.NoautomatizmAfterStimul=0 // не повторять, а дать сбросить сообщение
						потому как все сложно из-за синхронизации пульсов (js:sincronisationWithGo()),
						котоая тоже вызывает "set_img_action"
						Cброс NoautomatizmAfterStimul происходит по js:endNoautomatizmAfterStimul()
						после отработки сообщения в GET обработчике: r.FormValue("endNoautomatizmAfterStimul")
					*/
				}
				res, timeWait := psychic.WaitingPeriodForActions()
				if res {
					waitingPeriod = strconv.Itoa(timeWait)
				}
				var gaimMode = "0"
				if transfer.IsPsychicGameMode {
					gaimMode = "1"
				}
				var teachingMode = "0"
				if transfer.IsPsychicТeachingMode {
					teachingMode = "1"
				}
				p1 := gomeostas.GetCurGomeoStatus()
				p2 := gomeostas.GetCurContextActive()
				p3 := reflexes.GetCurrentConditionsStr()
				p4 := strconv.Itoa(psychic.CurrentMoodCondition)

				outStr += "#%#" + p1 + "#%#" + p2 +
					"#%# " + p3 + // чтобы постоянно была инфа о сочетаниях контекстов
					"#%#" + strconv.Itoa(brain.LifeTime) +
					"#%#" + reflexes.NoUnconditionRefles +
					"#%#" + waitingPeriod +
					"#%#" + psychic.GetPsichicReady() +
					"#%#" + p4 +
					"#%#" + gaimMode +
					"#%#" + psychic.GetExtandIndoForPult() +
					"#%#" + teachingMode

				brain.IsPultActivnost = false

				if strings.Contains(outStr, "zzz") { // для точки прерывания
					_, _ = fmt.Fprint(resp, outStr)
					return
				}

				_, _ = fmt.Fprint(resp, outStr)
				return
			}
			//////////////////////////////////////////////

			// установка параметров гомеостаза с Пульта:
			// задать параметры гомеостаза Beast
			setParamsId := r.FormValue("set_params")
			if len(setParamsId) > 0 {
				brain.IsPultActivnost = true
				id, _ := strconv.Atoi(setParamsId)
				gomeostas.SetCurGomeoParams(id, r.FormValue("params_val"))
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, "1")
				return
			}

			//  передача информации от Beast в Пульт различается идентификатором назначения информации перед самой информацией
			// информация для консоли ничинается с идентификатора назначения: "CONSOL:"
			if len(lib.WritePultConsolStr) > 0 {
				_, _ = fmt.Fprint(resp, "CONSOL:"+lib.WritePultConsolStr)
				lib.WritePultConsolStr = "" // очистка для новой порции
				return
			}

			/* Если есть непустой ActionsForPultStr, то выдать на Пульт действия
			информация о действиях Beast ничинается с идентификатора назначения: "ACTION:"
			*/
			if len(lib.ActionsForPultStr) > 0 {
				sctionsStr := lib.SharedReflexWithAutomatizm()
				if len(sctionsStr) > 0 {
					_, _ = fmt.Fprint(resp, "ACTION:"+sctionsStr)
				}
				lib.ActionsForPultStr = "" // очистка для новой порции
				return
			}

			// если ничего выше не было, то:
			// передача на Пульт сигнала готовности - когда нет других запросов - посылаетс сигнал на Пульт в function bot_answer(res)
			if word_sensor.IsReadyWordSensorLevel() {
				//идентификатор назначения информации: "READY"
				_, _ = fmt.Fprint(resp, "READY")
				return
			}
			_, _ = fmt.Fprint(resp, "POST")
		} else {
			brain.NotAllowAnyActions = true

			// Сформировать условные рефлексы на основе списка фраз-синонимов
			file_for_condition_reflexes := r.FormValue("file_for_condition_reflexes")
			if len(file_for_condition_reflexes) > 0 {
				reflexes.FormingConditionsRefleaxFromList(file_for_condition_reflexes)
				_, _ = fmt.Fprint(resp, "OK")
			}
		}
		//fmt.Println("EMPTY")
	}

	if r.Method == "GET" {
		// проверка активности Beast, аозвращает текущий brain.PulsCount
		brain.IsPultActivnost = true
		check_Beast_activnost := r.FormValue("check_Beast_activnost")
		if check_Beast_activnost == "1" {
			_, _ = fmt.Fprint(resp, brain.PulsCount)
			return
		}
		// остановка любой активности Beast
		brain.IsPultActivnost = true
		stop_activnost := r.FormValue("stop_activnost")
		if stop_activnost == "1" {
			isGlobalStopAllActivnost = true
			_, _ = fmt.Fprint(resp, "stop")
			return
		}
		// восстановление активности Beast
		start_activnost := r.FormValue("start_activnost")
		if start_activnost == "1" {
			isGlobalStopAllActivnost = false
			brain.IsPultActivnost = false
			_, _ = fmt.Fprint(resp, "active")
			return
		}
		// ЗОНА ОСОБЫХ ДЕЙСТВИЙ в период остановленной активности Beast:

		// Сохранить текущее состояние Beast
		save_all_memory := r.FormValue("save_all_memory")
		if save_all_memory == "1" {
			brain.IsPultActivnost = true
			if brain.SaveAll() {
				_, _ = fmt.Fprint(resp, "yes")
				brain.IsPultActivnost = false
				return
			}
			_, _ = fmt.Fprint(resp, "no")
			brain.IsPultActivnost = false
			return
		}
		// корректное выключение Beast
		bot_closing := r.FormValue("bot_closing")
		if len(bot_closing) > 0 {
			brain.IsPultActivnost = true
			cleanupFunc(bot_closing)
			return
		}
		// выключение по крестику окна браузера
		beforeunload := r.FormValue("beforeunload")
		if len(beforeunload) > 0 {
			testSTR := r.FormValue("testSTR")
			if len(testSTR) > 0 {
				// просто для тестирования
			}
			if noСloseWeght {
				return
			}
			brain.IsPultActivnost = true
			cleanupFunc("1") // "1" - сохранить все
			return
		}
		// на время перезагрузки НЕ закрывать прогу
		no_close_weght := r.FormValue("no_close_weght")
		if len(no_close_weght) > 0 {
			noСloseWeght = true
			_, _ = fmt.Fprint(resp, "!")
			//time.Sleep(2 * time.Second) // ждем 2 сек
			time.AfterFunc(2*time.Second, func() {
				noСloseWeght = false
			})
			return
		}

		// Формирование зеркальных автоматизмов на основе списка ответов
		mirror_making_fool := r.FormValue("mirror_making_fool")
		if len(mirror_making_fool) > 0 {
			res := psychic.FormingMirrorAutomatizmFromList(mirror_making_fool)
			_, _ = fmt.Fprint(resp, res)
			return
		}
		// Формирование зеркальных автоматизмов на основе общего шаблона
		mirror_making_temp := r.FormValue("mirror_making_temp")
		if len(mirror_making_temp) > 0 {
			res := psychic.FormingMirrorAutomatizmFromTempList(mirror_making_temp)
			_, _ = fmt.Fprint(resp, res)
			return
		}

		if !isGlobalStopAllActivnost {
			setExpParam := r.FormValue("set_exp_param") // экспорт данных
			if len(setExpParam) > 0 {
				brain.IsPultActivnost = true
				if setExpParam == "1" {
					IsExpTrue, expTxt := update.ExportFileUpdate([]int{1, 2, 3, 4, 5})
					if IsExpTrue == true {
						setExpParam = "yes|" + expTxt
					} else {
						setExpParam = "no|" + expTxt
					}
				}
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, setExpParam)
				return
			}

			setImpParam := r.FormValue("set_imp_param") // импорт данных
			if len(setImpParam) > 0 {
				brain.IsPultActivnost = true
				if setImpParam == "1" {
					IsImpParam, impTxt := update.ImportFileUpdate([]int{1, 2, 3, 4, 5})
					if IsImpParam == true {
						setImpParam = "yes|" + impTxt
					} else {
						setImpParam = "no|" + impTxt
					}
				}
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, setImpParam)
				return
			}

			get_reflex_tree := r.FormValue("get_reflex_tree")
			if get_reflex_tree == "1" {
				brain.IsPultActivnost = true
				tree := reflexes.GetReflexesTreeForPult()
				brain.IsPultActivnost = false
				if tree == "!!!" {
					return // запрет показа карты во время распознавания и записи
				}
				_, _ = fmt.Fprint(resp, tree)
				return
			}

			get_phrase_tree := r.FormValue("get_phrase_tree")
			if get_phrase_tree == "1" {
				brain.IsPultActivnost = true
				phTree := word_sensor.GetPhraseTreeForPult()
				brain.IsPultActivnost = false
				if phTree == "!!!" {
					return // запрет показа карты во время распознавания и записи
				}
				_, _ = fmt.Fprint(resp, phTree)
				return
			}

			get_words_list := r.FormValue("get_words_list")
			if get_words_list == "1" {
				brain.IsPultActivnost = true
				phTree := word_sensor.GetWordsListForPult()
				brain.IsPultActivnost = false
				if phTree == "!!!" {
					return // запрет показа карты во время распознавания и записи
				}
				_, _ = fmt.Fprint(resp, phTree)
				return
			}

			deleting_word := r.FormValue("deleting_word")
			if deleting_word == "1" {
				delete_word := r.FormValue("delete_word")
				deleteWord, _ := strconv.Atoi(delete_word)
				brain.IsPultActivnost = true
				word_sensor.DeleteWord(deleteWord)
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, "OK")
				return
			}

			get_word_tree := r.FormValue("get_word_tree")
			if get_word_tree == "1" {
				brain.IsPultActivnost = true
				phTree := word_sensor.GetWordTreeForPult()
				brain.IsPultActivnost = false
				if phTree == "!!!" {
					return // запрет показа карты во время распознавания и записи
				}
				_, _ = fmt.Fprint(resp, phTree)
				return
			}

			//
			/*xxxxxxx - заглушка от множестенных вызовов (в том числе при возврате по стеку от паники)


			 */
			set_action := r.FormValue("set_action")
			if len(set_action) > 0 && xxxxxxx == 0 {
				xxxxxxx = 1
				// нажимать кнопки не чаще раз в 1 секунду
				time.AfterFunc(1*time.Second, func() {
					xxxxxxx = 0
				})

				brain.IsPultActivnost = true
				//ActionsForPultOldStr = lib.ActionsForPultOldStr
				action_sensor.IsPultAction()
				enegry, _ := strconv.Atoi(r.FormValue("food_portion"))
				// здесь включение игрового режима IsPsychicGameMode при нажатии на Поиграть
				action_sensor.SetActionFromPult(set_action, enegry)

				// активировать дерево действием
				reflexes.ActiveFromAction()
				brain.IsPultActivnost = false
				// и если есть ответ, то тут же выдать на ПультL
				var answerStr = ""
				if len(lib.ActionsForPultStr) > 5 {
					lib.ActionsForPultStr = lib.SharedReflexWithAutomatizm()
					if len(lib.ActionsForPultStr) > 5 {
						answerStr = lib.ActionsForPultStr
					}
					lib.ActionsForPultStr = "" // очистка для новой порции
				}
				_, _ = fmt.Fprint(resp, answerStr)
				return
			}

			// установка-снятие игрового режима в pult_base_contexts.php по function end_game_mode()
			is_game_mode := r.FormValue("is_game_mode")
			if len(is_game_mode) > 0 {
				if is_game_mode == "1" {
					transfer.IsPsychicGameMode = true // переключатель игрового режима
				} else {
					transfer.IsPsychicGameMode = false // переключатель игрового режима
				}
				_, _ = fmt.Fprint(resp, "ok")
				return
			}

			// установка-снятие учительского режима в pult_base_contexts.php по function end_teaching_mode()
			is_teaching_mode := r.FormValue("is_teaching_mode")
			if len(is_teaching_mode) > 0 {
				if is_teaching_mode == "1" {
					transfer.IsPsychicТeachingMode = true // переключатель учительского режима
				} else {
					transfer.IsPsychicТeachingMode = false // переключатель учительского режима
				}
				_, _ = fmt.Fprint(resp, "ok")
				return
			}

			// снятие состояния Хорошо и Плохо
			close_whell_bad_mode := r.FormValue("close_whell_bad_mode")
			if len(close_whell_bad_mode) > 0 {
				// Снять режимы Плохо и Хорошо
				gomeostas.CliarWellBad()
				_, _ = fmt.Fprint(resp, "ok")
				return
			}

			/* // проверка игрового режима
			check_game_mode := r.FormValue("check_game_mode")
			if len(check_game_mode) > 0 {
				if transfer.IsPsychicGameMode{
					_, _ = fmt.Fprint(resp, "1")
				}else{
					_, _ = fmt.Fprint(resp, "0")
				}
				return
			}*/

			cliner_time_condition_reflex := r.FormValue("cliner_time_condition_reflex")
			if len(cliner_time_condition_reflex) > 0 {
				isGlobalStopAllActivnost = true
				ret := reflexes.ClinerTimeConditionReflex()
				isGlobalStopAllActivnost = false
				_, _ = fmt.Fprint(resp, ret)
				return
			}

			get_condition_reflex_info := r.FormValue("get_condition_reflex_info")
			if len(get_condition_reflex_info) > 0 {
				base := r.FormValue("limitBasicID")
				limitBasicID, _ := strconv.Atoi(base)
				ref := reflexes.GetConditionReflexInfo(limitBasicID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_automatizm_list_info := r.FormValue("get_automatizm_list_info")
			if len(get_automatizm_list_info) > 0 {
				lpage := r.FormValue("limitBasicID")
				limitPage, _ := strconv.Atoi(lpage)
				ref := psychic.GetAutomatizmInfo(limitPage)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			unblocking_all := r.FormValue("unblocking_all")
			if len(unblocking_all) > 0 {
				psychic.UnblockingAllAtmtzms()
				_, _ = fmt.Fprint(resp, "ok")
				return
			}

			get_next_actions_info_list := r.FormValue("get_next_actions_info_list")
			if len(get_next_actions_info_list) > 0 {
				ref := psychic.GetNextActionsInfoList()
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_trigger_info := r.FormValue("get_trigger_info")
			if len(get_trigger_info) > 0 {
				triggerID, _ := strconv.Atoi(r.FormValue("triggerID"))
				ref := reflexes.GetTreeAutomatizmTriggersInfo(triggerID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_sequence_info := r.FormValue("get_sequence_info")
			if len(get_sequence_info) > 0 {
				autmzmID, _ := strconv.Atoi(r.FormValue("autmzmID"))
				ref := psychic.GetAutomatizmSequenceInfo(autmzmID, false)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_show_problem_tree_info := r.FormValue("get_show_problem_tree_info")
			if len(get_show_problem_tree_info) > 0 {
				pID, _ := strconv.Atoi(r.FormValue("mautmzmID"))
				ref := psychic.ShowProblemTreeInfo(pID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_emotion_info := r.FormValue("get_emotion_info")
			if len(get_emotion_info) > 0 {
				emotionID, _ := strconv.Atoi(r.FormValue("emotionID"))
				ref := psychic.GetStrnameFromBaseImageID(emotionID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_next_actions_info := r.FormValue("get_next_actions_info")
			if len(get_next_actions_info) > 0 {
				nextID, _ := strconv.Atoi(r.FormValue("nextID"))
				ref := psychic.GetNextActionsInfo(nextID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_object_info := r.FormValue("get_object_info")
			if len(get_object_info) > 0 {
				objectID, _ := strconv.Atoi(r.FormValue("objectID"))
				ref := psychic.GetStrnameFromobjectID(objectID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			cliner_atmtzm_block := r.FormValue("cliner_atmtzm_block")
			if len(cliner_atmtzm_block) > 0 {
				atmtzmID, _ := strconv.Atoi(r.FormValue("atmtzmID"))
				ref := psychic.UnblockAutomatizmID(atmtzmID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_dominant_info := r.FormValue("get_dominant_info")
			if len(get_dominant_info) > 0 {
				dID, _ := strconv.Atoi(r.FormValue("atmtzmID"))
				ref := psychic.DominantaInfoStr(dID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_self_perception_info := r.FormValue("get_self_perception_info")
			if len(get_self_perception_info) > 0 {
				ref := psychic.GetSelfPerceptionInfo()
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_self_conscience_info := r.FormValue("get_self_conscience_info")
			if len(get_self_conscience_info) > 0 {
				ref := psychic.GetConscienceInfo()
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_cycle_log_info := r.FormValue("get_cycle_log_info")
			if len(get_cycle_log_info) > 0 {
				cID, _ := strconv.Atoi(get_cycle_log_info)
				ref := psychic.GetCycleLocInfo(cID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_automatizm_tree := r.FormValue("get_automatizm_tree")
			if len(get_automatizm_tree) > 0 {
				base := r.FormValue("limitBasicID")
				limitBasicID, _ := strconv.Atoi(base)
				ref := psychic.GetAutomatizmTreeForPult(limitBasicID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			get_node_automatizms := r.FormValue("get_node_automatizms")
			if len(get_node_automatizms) > 0 {
				nodeID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetNodesAutomatismsInfo(nodeID) //GetAutomatizmForNodeInfo(nodeID)- пишет в лог
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			get_automatizm := r.FormValue("get_automatizm")
			if len(get_automatizm) > 0 {
				ID, _ := strconv.Atoi(r.FormValue("autID"))
				atmz, ok := psychic.ReadeAutomatizmFromId(ID)
				if ok {
					ref := psychic.GetAutomotizmActionsString(atmz, false, true)
					_, _ = fmt.Fprint(resp, ref)
				}
				return
			}

			get_problem_tree_node := r.FormValue("get_problem_tree_node")
			if len(get_problem_tree_node) > 0 {
				nodeID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetProblemTreeForNodeInfo(nodeID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_node_aut_tree_node := r.FormValue("get_node_aut_tree_node")
			if len(get_node_aut_tree_node) > 0 {
				nodeID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetAutomatizmNodeTreeForPult(nodeID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_mental_automatizm_tree := r.FormValue("get_mental_automatizm_tree")
			if len(get_mental_automatizm_tree) > 0 {
				base := r.FormValue("limit")
				limit, _ := strconv.Atoi(base)
				ref := psychic.GetMentalAutomatizmTreeForPult(limit)
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			get_node_mental_situations := r.FormValue("get_node_mental_situations")
			if len(get_node_mental_situations) > 0 {
				nodeID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetMentalSituationsForNodeInfo(nodeID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_node_mental_automatizm := r.FormValue("get_node_mental_automatizm")
			if len(get_node_mental_automatizm) > 0 {
				//maID,_:=strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetMentalAutomatizmForPult()
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_ment_model_index := r.FormValue("get_ment_model_index")
			if len(get_ment_model_index) > 0 {
				maID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetModelExtremImportanceInfo(maID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_node_mental_purpose := r.FormValue("get_node_mental_purpose")
			if len(get_node_mental_purpose) > 0 {
				nodeID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetMentalPurposeForNodeInfo(nodeID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_node_theme_image := r.FormValue("get_node_theme_image")
			if len(get_node_theme_image) > 0 {
				nodeID, _ := strconv.Atoi(r.FormValue("autNodeID"))
				ref := psychic.GetMentalThemeForNodeInfo(nodeID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_mental_priblem_tree := r.FormValue("get_mental_priblem_tree")
			if len(get_mental_priblem_tree) > 0 {
				base := r.FormValue("limit")
				limit, _ := strconv.Atoi(base)
				ref := psychic.GetMentalPriblemTreeForPult(limit)
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			/*
				get_mental_undestanding_models_info := r.FormValue("get_mental_undestanding_models_info")
				if len(get_mental_undestanding_models_info) > 0 {
					ref := psychic.GetMentalUndestandingModelsForPult()
					_, _ = fmt.Fprint(resp, ref)
					return
				}
			*/
			get_rulles_list_info := r.FormValue("get_rulles_list_info")
			if get_rulles_list_info == "1" {
				rulles := psychic.GetCur100lastRules(0)
				_, _ = fmt.Fprint(resp, rulles)
				return
			}

			get_undestand_model := r.FormValue("get_undestand_model")
			if get_undestand_model == "1" {
				ids := r.FormValue("objID")
				id, _ := strconv.Atoi(ids)
				model := psychic.Get_undestand_model_from_object(id)
				_, _ = fmt.Fprint(resp, model)
				return
			}

			clian_episodic_memory := r.FormValue("clian_episodic_memory")
			if clian_episodic_memory == "1" {
				psychic.ClianEpisodicMemory()
				_, _ = fmt.Fprint(resp, "did")
				return
			}

			clian_ment_episodic_memory := r.FormValue("clian_ment_episodic_memory")
			if clian_ment_episodic_memory == "1" {
				psychic.ClianMentalEpisodicMemory()
				_, _ = fmt.Fprint(resp, "did")
				return
			}

			get_mental_rulles_list_info := r.FormValue("get_mental_rulles_list_info")
			if get_mental_rulles_list_info == "1" {
				rulles := psychic.GetMentallastRules()
				_, _ = fmt.Fprint(resp, rulles)
				return
			}

			get_mental_action_info := r.FormValue("get_mental_action_info")
			if len(get_mental_action_info) > 0 {
				//actID,_:=strconv.Atoi(get_mental_action_info)
				rulles := "НЕТ ПОДДЕРЖКИ psychic.GetMentalActionInfo - устарело." //psychic.GetMentalActionInfo(actID)
				_, _ = fmt.Fprint(resp, rulles)
				return
			}

			get_mental_goNext_info := r.FormValue("get_mental_goNext_info")
			if len(get_mental_goNext_info) > 0 {
				//actID,_:=strconv.Atoi(get_mental_goNext_info)
				rulles := "НЕТ ПОДДЕРЖКИ psychic.GetMentalActionsString - устарело." //psychic.GetMentalActionsString(actID)
				_, _ = fmt.Fprint(resp, rulles)
				return
			}

			// инфа о крутящихся активных циклах
			get_cycle_info := r.FormValue("get_cycle_info")
			if len(get_cycle_info) > 0 {
				cID, _ := strconv.Atoi(get_cycle_info)
				rulles := psychic.GetCycleStrInfo(cID)
				_, _ = fmt.Fprint(resp, rulles)
				return
			}

			// инфо о инфо-функции
			get_mental_func_info := r.FormValue("get_mental_func_info")
			if len(get_mental_func_info) > 0 {
				fID, _ := strconv.Atoi(get_mental_func_info)
				rulles := psychic.GetgetInfoFuncInfoStr(fID)
				_, _ = fmt.Fprint(resp, rulles)
				return
			}

			get_atmzm_tree_info := r.FormValue("get_atmzm_tree_info")
			if get_atmzm_tree_info == "1" {
				objID, _ := strconv.Atoi(r.FormValue("objID"))
				ref := psychic.GetAtmzmTreeInfo(objID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			get_undstg_tree_info := r.FormValue("get_undstg_tree_info")
			if get_undstg_tree_info == "1" {
				objID, _ := strconv.Atoi(r.FormValue("objID"))
				ref := psychic.GetUndstgTreeInfo(objID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			get_ment_atmzm_info := r.FormValue("get_ment_atmzm_info")
			if get_ment_atmzm_info == "1" {
				objID, _ := strconv.Atoi(r.FormValue("objID"))
				ref := psychic.GetMentAtmzmInfo(objID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			make_automatizms_from_reflexes := r.FormValue("make_automatizms_from_reflexes")
			if len(make_automatizms_from_reflexes) == 1 {
				ref := reflexes.RunMakeAutomatizmsFromReflexes()
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			make_automatizms_from_genetic_reflexes := r.FormValue("make_automatizms_from_genetic_reflexes")
			if len(make_automatizms_from_genetic_reflexes) == 1 {
				ref := reflexes.RunMakeAutomatizmsFromGeneticReflexes()
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			// объект значимости
			get_mental_importance_list_info := r.FormValue("get_mental_importance_list_info")
			if get_mental_importance_list_info == "1" {
				ref := psychic.GetImportanceToPult()
				_, _ = fmt.Fprint(resp, ref)
				return
			}
			get_show_cyckle_info := r.FormValue("get_show_cyckle_info")
			if get_show_cyckle_info == "1" {
				cID, _ := strconv.Atoi(r.FormValue("objID"))
				ref := psychic.GetCycleInfo(cID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_importance_object_info := r.FormValue("get_importance_object_info")
			if get_importance_object_info == "1" {
				objID, _ := strconv.Atoi(r.FormValue("objID"))
				//objType, _ := strconv.Atoi(r.FormValue("objType"))
				ref := psychic.GetImportanceObjectInfo(objID)
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			get_dominants_list_info := r.FormValue("get_dominants_list_info")
			if get_dominants_list_info == "1" {
				ref := psychic.GetDominantsListToPult()
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			//получить фразы, используемые в автоматизмах для иконки выбора Пульта
			conditions_words_basic := r.FormValue("conditions_words_basic")
			if conditions_words_basic == "1" {
				// против concurrent map iteration and map write
				brain.IsPultActivnost = true
				bID := r.FormValue("basicID")
				bID = strings.Trim(bID, " ")
				basicID, _ := strconv.Atoi(bID)
				contexts := r.FormValue("contexts")
				ref := psychic.GetAutomatizmPraseList(basicID, contexts)
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, ref)
				return
			}

			// обнулить параметры гомеостаза Beast
			cliner_gomeo_pars := r.FormValue("cliner_gomeo_pars")
			if len(cliner_gomeo_pars) > 0 {
				brain.IsPultActivnost = true
				value, _ := strconv.ParseFloat(cliner_gomeo_pars, 64)
				gomeostas.ClinerAllGomeoParams(value)
				brain.IsPultActivnost = false
				_, _ = fmt.Fprint(resp, "1")
				return
			}

			// завершить период ожидания по щелчку на плашке
			stop_waiting_period := r.FormValue("stop_waiting_period")
			if len(stop_waiting_period) > 0 {
				psychic.StopWaitingWeriodFromOperator() // завершить цепочку темы
				_, _ = fmt.Fprint(resp, "1")
				return
			}

			end_noautomatizm := r.FormValue("end_noautomatizm")
			if len(end_noautomatizm) > 0 {
				psychic.NoautomatizmAfterStimul = 0
				_, _ = fmt.Fprint(resp, "ok_end")
				return
			}

			_, _ = fmt.Fprint(resp, "GET")
		}
	}

}

// инициализация
func init() {
	lib.GetMainPathExeFile()
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	// тестирование комбинаций. Если бы время работы было приемлемо,
	//то можно было бы запускать процесс из Пульта в меню Инструменты (шестеренка)
	// 	tools.MakeContextCombinations()
	/*
		res:=lib.DoublesRemove("3|Действие: <b><span style=\"font-size:14px;\">кричит</span>, <span style=\"font-size:14px;\">дерется</span>, <span style=\"font-size:14px;\">опасается</span></b><br><span style=\"font-size:15px;color:#000000\">Энергичность: <b>Средне (сила=8)</b></span><br>||3|Действие: <b><span style=\"font-size:14px;\">ОРЕТ</span>, <span style=\"font-size:14px;\">КУСАЕТСЯ</span>, <span style=\"font-size:14px;\">опасается</span></b><br><span style=\"font-size:15px;color:#000000\">Энергичность: <b>Средне (сила=6)</b></span><br>||3|Действие: <b><span style=\"font-size:14px;\">кричит</span>, <span style=\"font-size:14px;\">дерется</span>, <span style=\"font-size:14px;\">опасается</span></b><br><span style=\"font-size:15px;color:#000000\">Энергичность: <b>Средне (сила=6)</b></span><br>")
		if len(res)>0{}
	*/
}

func main() {

	address := lib.ReadFileContent(lib.GetMainPathExeFile() + "/common/linking_address.txt")
	address = strings.TrimSpace(address)[7:]

	brain.RunInitialisation() // init.go
	brain.Puls()

	http.HandleFunc("/", receiveSend)
	_ = http.ListenAndServe(address, nil)
	//	fmt.Println("Сервер запущен...")

}

// отключение Beast - по запросу со страницы Пульта
func cleanupFunc(typeClosing string) {
	lib.WritePultConsol("Beast вырубается.")
	fmt.Print("ПОСЛЕДНИЕ ДЕЙСТВИЯ ПЕРЕД ЗАКРЫВАНИЕМ ПРОГРАММЫ")

	if typeClosing == "1" {
		// записать текущее состояние Дерева Моделей и Эпизодическую память
		//	brain.PrepareBeforCloseTreeModel()
		/* если внезапно откобчить мозг человека, то из памяти пропадет все, что было в последние полчаса
		так что просто записывать PrepareBeforCloseTreeModel() раз в 10 минут и при создании нового узла дерева
		*/
		brain.SaveAll()
	}

	os.Exit(1)
}

// здесь могут быть функции для обеспечения связи между пакетами чтобы избегать цикличного импорта

____________________________________________

BOT/tools/combinations.go
____________________________________________
// Package combinations provides a method to generate all combinations out of a given string array.
package tools

import "math/bits"

// All returns all combinations for a given string array.
// This is essentially a powerset of the given set except that the empty set is disregarded.
func All(set []string) (subsets [][]string) {
	length := uint(len(set))

	// Go through all possible combinations of objects
	// from 1 (only first object in subset) to 2^length (all objects in subset)
	for subsetBits := 1; subsetBits < (1 << length); subsetBits++ {
		var subset []string

		for object := uint(0); object < length; object++ {
			// checks if object is contained in subset
			// by checking if bit 'object' is set in subsetBits
			if (subsetBits>>object)&1 == 1 {
				// add object to subset
				subset = append(subset, set[object])
			}
		}
		// add subset to subsets
		subsets = append(subsets, subset)
	}
	return subsets
}

// Combinations returns combinations of n elements for a given string array.
// For n < 1, it equals to All and returns all combinations.
func Combinations(set []string, n int) (subsets [][]string) {
	length := uint(len(set))

	if n > len(set) {
		n = len(set)
	}

	// Go through all possible combinations of objects
	// from 1 (only first object in subset) to 2^length (all objects in subset)
	for subsetBits := 1; subsetBits < (1 << length); subsetBits++ {
		if n > 0 && bits.OnesCount(uint(subsetBits)) != n {
			continue
		}

		var subset []string

		for object := uint(0); object < length; object++ {
			// checks if object is contained in subset
			// by checking if bit 'object' is set in subsetBits
			if (subsetBits>>object)&1 == 1 {
				// add object to subset
				subset = append(subset, set[object])
			}
		}
		// add subset to subsets
		subsets = append(subsets, subset)
	}
	return subsets
}

____________________________________________

BOT/tools/combinations_maker.go
____________________________________________
/*
Генерация сочетаний для редактора б.рефлексов.


*/

package tools

import (
	"BOT/lib"
	"strconv"
)

func init(){


}
/////////////////////////////////////////////////////
// найти все сочетания ряда чисел от 0 до максимального подряд (без перемешивания
// limit - максимальное число элементов в сочетании
func GetAllCombinationsOfSeriesNumbers(max int,limit int)([][]int){
	var out [][]int
	for i := 0; i < max; i++ {
		var arr []int
		end:=limit+i
		if i > max-limit{
			end=max
		}
		for j := i; j < end; j++ {
			arr=append(arr,j)
		}
		out=append(out,arr)
		// подсочетания
		var arr0 []int
		for n := 2; n < len(arr); n++ {
			for m := 0; m < n; m++ {
				arr0=append(arr0,arr[m])
			}
			out=append(out,arr0)
		}
	}
	return out
}
////////////////////////////////////////////////////


// найти все сочетания ряда чисел от 0 до максимального без перемешивания
// limit - максимальное число элементов в сочетании
func GetAllCombinationsNumbers(max int,limit int)([][]int){
	var out [][]int
	var series []string
	for i := 0; i < max; i++ {
		series=append(series,strconv.Itoa(i))
	}
	comb:=Combinations(series,limit)
	for _, v := range comb {
		var arr []int
		for n := 0; n < len(v); n++ {
			nun,_:=strconv.Atoi(v[n])
			arr=append(arr,nun)
		}
		out=append(out,arr)
	}
	return out
}
////////////////////////////////////////////////////



func MakeContextCombinations() {
	var cellArr []string
	for i := 1; i < 18; i++ {
			cellArr=append(cellArr,strconv.Itoa(i))
	}
	arr:=Combinations(cellArr,1)
	//arr:=All(cellArr)

	out:=""

	for _, v := range arr {
		for i := 0; i < len(v); i++ {
			out+=v[i]+","
		}
		out+="\r\n"
	}

	lib.WriteFileContent(lib.GetMainPathExeFile()+"/combination.txt", out)
	if len(out)>0{

	}
	return
}

var nid []int
var outArr []string
var cols int
var gCArr=make(map[int][]string)
// сейчас вызов из main.init()
/*
func MakeContextCombinations() {

	gCArr = gomeostas.GomeostazActivnostArr

	cols = len(gCArr[1])
//	rows := len(gCArr)
//	nid = make([]int, rows)
	arr1 := gCArr[1];
	arr2 := gCArr[2];
	arr3 := gCArr[3];
	arr4 := gCArr[4];
	arr5 := gCArr[5];
	arr6 := gCArr[6];
	arr7 := gCArr[7];
	arr8 := gCArr[8];
	var out = "";
	for n1:= 0; n1 < cols; n1++ {
		for n2:= 0; n2 < cols; n2++ {
			for n3:= 0; n3 < cols; n3++ {
				for n4:= 0; n4 < cols; n4++ {
					for n5:= 0; n5 < cols; n5++ {
						for n6:= 0; n6 < cols; n6++ {
							for n7:= 0; n7 < cols; n7++ {
								for n8:= 0; n8 < cols; n8++ {
out +=arr1[n1]+"|"+arr2[n2]+"|"+arr3[n3]+"|"+arr4[n4]+"|"+arr5[n5]+"|"+arr6[n6]+"|"+arr7[n7]+"|"+arr8[n8]
								}
							}
						}
					}
				}
			}
		}
	}

	// удаляем повторы из каждого сочетания
	var comb = make([][]int, len(outArr))
	for k, v := range outArr {
		p := strings.Split(v, ",")
		for i := 0; i < len(p); i++ {
			id, _ := strconv.Atoi(p[i])
			comb[k] = append(comb[k], id)
		}
		comb[k] = lib.RemoveDuplicateValues(comb[k])
		sort.Ints(comb[k])
	}


	return
}
 */


/////////////////////////////////////////////////////

/* // перебор с использованием проф. библиотеки
var cellArr []string
cols:=len(gCArr[1])
rows:=len(gCArr)
	for i := 1; i < rows; i++ {
		for j := 0; j < cols; j++ {
			cellArr=append(cellArr,gCArr[i][j])
			//cellArr=append(cellArr,strconv.Itoa(i*j))
		}
	}

	// Время выполнения для перебора 49  - очень большое...
	out:=All(cellArr)
	//out:=Combinations(cellArr,3)
	if len(out)>0{

	}

return
}
 */
____________________________________________

