{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 2\n",
      "2 2\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Traceback (most recent call last):\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 388, in process\n",
      "    proxy(*args, **kwargs)\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 228, in __call__\n",
      "    return mtd(*args, **kwargs)\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1308, in _handle_resize\n",
      "    self._init_draw()\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1750, in _init_draw\n",
      "    self._draw_frame(next(self.new_frame_seq()))\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1772, in _draw_frame\n",
      "    self._drawn_artists = self._func(framedata, *self._args)\n",
      "  File \"<ipython-input-3-63ec1324f14b>\", line 268, in updatefig\n",
      "    im1=plt.imshow(Zm, cmap=plt.get_cmap('magma').with_extremes(bad='#101020'))\n",
      "AttributeError: 'ListedColormap' object has no attribute 'with_extremes'\n",
      "Traceback (most recent call last):\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 388, in process\n",
      "    proxy(*args, **kwargs)\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py\", line 228, in __call__\n",
      "    return mtd(*args, **kwargs)\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1308, in _handle_resize\n",
      "    self._init_draw()\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1750, in _init_draw\n",
      "    self._draw_frame(next(self.new_frame_seq()))\n",
      "  File \"C:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\animation.py\", line 1772, in _draw_frame\n",
      "    self._drawn_artists = self._func(framedata, *self._args)\n",
      "  File \"<ipython-input-3-63ec1324f14b>\", line 268, in updatefig\n",
      "    im1=plt.imshow(Zm, cmap=plt.get_cmap('magma').with_extremes(bad='#101020'))\n",
      "AttributeError: 'ListedColormap' object has no attribute 'with_extremes'\n"
     ]
    }
   ],
   "source": [
    "%matplotlib tk\n",
    "import time, math\n",
    "from random import random\n",
    "import numpy as np\n",
    "from IPython import display\n",
    "from IPython.display import clear_output\n",
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.animation as animation\n",
    "\n",
    "# Генерация карт по картинке\n",
    "\n",
    "Terrain=np.sign(np.array(Image.open(\"test.png\"))[:,:,0])\n",
    "ShowTerrain=Terrain*1.0\n",
    "Flow=Store=TransportActivity=Conc=Terrain*0.0\n",
    "Dir=Terrain*0\n",
    "\n",
    "# Генерация случайных препятствий\n",
    "\n",
    "#for i in range(100):\n",
    "#    Terrain[int(random()*Live.shape[0]-2),int(random()*Live.shape[1]-2)]=0\n",
    "\n",
    "# Настройка параметров клетки\n",
    "    \n",
    "START_COMPLETENESS = 1\n",
    "#GROW_COMPLETENESS = 7\n",
    "REP_COMPLETENESS = 20\n",
    "#MAX_COMPLETENESS = 100\n",
    "\n",
    "# Настройка параметров распространеняи сигнала об активном строительстве \n",
    "\n",
    "INCOMPLETENESS_SIGNAL_EMISSION_RATIO = 0.1\n",
    "INCOMPLETENESS_SIGNAL_DIFFUSION_RATIO = 0.45\n",
    "INCOMPLETENESS_SIGNAL_DECAY_RATIO = 0.9\n",
    "INCOMPLETENESS_SIGNAL_THRESHOLD = 1\n",
    "\n",
    "# Настройка бобмбардировки    \n",
    "    \n",
    "BOMB_MIN=0\n",
    "BOMB_START=60000\n",
    "BOMB_MAX=1\n",
    "BOMB_SIZE=2\n",
    "b=BOMB_MIN\n",
    "\n",
    "# Выбор способа отображения цифровыми клавишами 0-5\n",
    "\n",
    "def on_press(event):\n",
    "    global DISPLAY\n",
    "    global ax\n",
    "    global plt\n",
    "    DISPLAY=int(event.key)\n",
    "    if DISPLAY==0:\n",
    "        ax.set_title('Terrain') \n",
    "    elif DISPLAY==1:\n",
    "        ax.set_title('Incompleteness Signal Concentration')\n",
    "    elif DISPLAY==2:\n",
    "        ax.set_title('Incompleteness Signal Direction')\n",
    "    elif DISPLAY==3:\n",
    "        ax.set_title('Transport Direction')\n",
    "    elif DISPLAY==4:\n",
    "        ax.set_title('Storage')\n",
    "    elif DISPLAY==5:\n",
    "        ax.set_title('Transport Activity')\n",
    "    plt.draw()\n",
    "\n",
    "# Настройка графического отображения\n",
    "\n",
    "fig=plt.figure()#(figsize=(7, 5))\n",
    "ax=fig.add_subplot(1, 1, 1)\n",
    "im1=plt.imshow(ShowTerrain, cmap=plt.get_cmap('bone'), animated=True)\n",
    "fig.canvas.mpl_connect('key_press_event', on_press)\n",
    "DISPLAY=0\n",
    "ax.set_title('Terrain')      \n",
    "writer = animation.writers['ffmpeg'](fps=15, metadata=dict(artist='Me'), bitrate=1800)\n",
    "framecounter=0\n",
    "\n",
    "# Словарь для хранения клеток по координатам\n",
    "\n",
    "cells={}\n",
    "    \n",
    "class Cell():\n",
    "    def __init__(self,x,y):\n",
    "        self.x=x\n",
    "        self.y=y\n",
    "        self.productivity=1\n",
    "        self.transportivity=10\n",
    "        self.completeness=START_COMPLETENESS\n",
    "        self.conc=0\n",
    "        self.flow=0\n",
    "        self.storage=0\n",
    "        cells[(self.x,self.y)] = self\n",
    "        self.refresh()\n",
    "    def refresh(self):\n",
    "        if self.completeness<1:\n",
    "            self.destroy()\n",
    "        addconc=(REP_COMPLETENESS-self.completeness)*INCOMPLETENESS_SIGNAL_EMISSION_RATIO\n",
    "        if self.completeness>=REP_COMPLETENESS:\n",
    "            addconc=0\n",
    "        self.conc+=addconc\n",
    "        self.conc*=INCOMPLETENESS_SIGNAL_DECAY_RATIO\n",
    "    def destroy(self):\n",
    "        del cells[(self.x,self.y)]\n",
    "\n",
    "# Создаем первоначальную клетку\n",
    "        \n",
    "a=Cell(20,20)\n",
    "a.completeness=REP_COMPLETENESS\n",
    "a.refresh()\n",
    "\n",
    "# Расчеты в каждом кадре\n",
    "\n",
    "def updatefig(*args):\n",
    "    global b,TransportActivity,ShowTerrain,Store,framecounter\n",
    "    \n",
    "    # Очистка карт для визуализации\n",
    "    \n",
    "    TransportActivity=Terrain*0.0\n",
    "    ShowTerrain=Terrain*1\n",
    "    Store=Terrain*0.0\n",
    "    Conc=Terrain*0.0\n",
    "       \n",
    "    clist=list(cells.values())\n",
    "    for c in clist:       # Расчеты для каждой клетки\n",
    "        c.refresh()\n",
    "        \n",
    "        # Отображение различных параметров клетки        \n",
    "        Store[c.x,c.y]=c.storage\n",
    "        ShowTerrain[c.x,c.y]=c.completeness\n",
    "        Conc[c.x,c.y]=c.conc\n",
    "               \n",
    "        if c.completeness>=REP_COMPLETENESS:    # Если клетка зрелая, то:\n",
    "            c.storage+=c.productivity           # Производство объектов на склад\n",
    "            \n",
    "            # Составляем список соседних участков с учетом границ карты\n",
    "            \n",
    "            neighbors=[]\n",
    "            if c.x>0:\n",
    "                neighbors.append((c.x-1,c.y))\n",
    "            if c.x<Terrain.shape[0]-1:\n",
    "                neighbors.append((c.x+1,c.y))\n",
    "            if c.y>0:\n",
    "                neighbors.append((c.x,c.y-1))\n",
    "            if c.y<Terrain.shape[1]-1:\n",
    "                neighbors.append((c.x,c.y+1))   \n",
    "\n",
    "            candidatemax=0\n",
    "            concmax=0\n",
    "            fx=0\n",
    "            fy=0\n",
    "            cneed=None\n",
    "            \n",
    "            for (nx,ny) in neighbors:                       # Для каждого участка из списка соседей\n",
    "                if (nx,ny) in cells.keys():                 # Если на нем есть клетка   \n",
    "                    \n",
    "                    # Расчет градиента сигнала роста\n",
    "                    \n",
    "                    f=cells[(nx,ny)]\n",
    "                    vconc=f.conc-c.conc\n",
    "                    fx+=vconc*(nx-c.x)\n",
    "                    fy+=vconc*(ny-c.y)\n",
    "                    \n",
    "                    # Выявление соседа с наибольшей концентрацией сигнала роста\n",
    "                    \n",
    "                    if f.conc>concmax:\n",
    "                        cneed=f\n",
    "                        needer=int((nx-c.x)+2*(ny-c.y)+3)\n",
    "                        if needer==5:\n",
    "                            needer=3\n",
    "                        concmax=f.conc\n",
    "                    \n",
    "                    # Диффузия сигнала роста\n",
    "                        \n",
    "                    aconc=(c.conc+f.conc)/2\n",
    "                    dconc=(aconc-c.conc)*INCOMPLETENESS_SIGNAL_DIFFUSION_RATIO   \n",
    "                    c.conc+=dconc\n",
    "                    f.conc-=dconc\n",
    "\n",
    "                    # Выявление наиболее готовой недостроенной соседней клетки\n",
    "                    \n",
    "                    if f.completeness<REP_COMPLETENESS: \n",
    "                        if candidatemax<f.completeness:\n",
    "                            candidatemax=f.completeness\n",
    "                            candidate=(nx,ny)\n",
    "                \n",
    "                # Если на участке нет клетки, проверяем можно ли там построить новую\n",
    "                # Постройка новых клеток имеет меньший приоритет, чем достройка начатых\n",
    "                \n",
    "                else:                                \n",
    "                    if Terrain[nx,ny]>0:\n",
    "                        if candidatemax<1:\n",
    "                            candidate=(nx,ny)\n",
    "                            candidatemax=1\n",
    "            \n",
    "            # Отображение на картах направления транспорта и градиента сигнала роста\n",
    "            \n",
    "            if concmax>0:\n",
    "                Dir[c.x,c.y]=needer\n",
    "                Flow[c.x,c.y]=math.atan2(fx,fy)\n",
    "            \n",
    "           \n",
    "            if candidatemax>0:                   # Если есть недостроенные или пустые участки\n",
    "                if candidatemax==1:              # Если есть только пустые\n",
    "                    cneed=Cell(candidate[0],candidate[1])  # Создать клетку\n",
    "                else:                                      # Иначе\n",
    "                    cneed=cells[(candidate[0],candidate[1])] # Помогаем строящейся клетке\n",
    "            \n",
    "            # Транспорт объектов в соседние клетки\n",
    "                    \n",
    "            if cneed:\n",
    "                \n",
    "                # Установим объем грузоперевозок в зависимости от концентрации сигнала роста\n",
    "                # и ограничим его минимум и максимум\n",
    "                \n",
    "                kgr=(cneed.conc)/INCOMPLETENESS_SIGNAL_THRESHOLD\n",
    "                if kgr<0:\n",
    "                    kgr=0\n",
    "                if kgr>1:\n",
    "                    kgr=1\n",
    "                \n",
    "                # Если запасы не меньше, чем планируемый экспорт,\n",
    "                # то отправим сколько планировали\n",
    "                \n",
    "                if c.storage>=c.transportivity*kgr:\n",
    "                    cneed.completeness+=c.transportivity*kgr         # Передача\n",
    "                    TransportActivity[c.x,c.y]+=c.transportivity*kgr # Отображение на карте перевозок\n",
    "                    c.storage-=c.transportivity*kgr                  # Удаление со склада\n",
    "\n",
    "                # Иначе отправим сколько можем\n",
    "                    \n",
    "                else:\n",
    "                    cneed.completeness+=c.storage         # Передача \n",
    "                    TransportActivity[c.x,c.y]+=c.storage # Отображение на карте перевозок   \n",
    "                    c.storage=0                           # Удаление со склада\n",
    "                \n",
    "                # Если получатель принял больше чем нужно для строительства, \n",
    "                # излишки у него складируются\n",
    "                # (на следующем ходу он может передать их дальше)\n",
    "                    \n",
    "                if cneed.completeness>REP_COMPLETENESS:\n",
    "                    cneed.storage+=cneed.completeness-REP_COMPLETENESS\n",
    "                    cneed.completeness=REP_COMPLETENESS\n",
    "                cneed.refresh()\n",
    "         \n",
    "    # Бомбардировка\n",
    "                \n",
    "    if len(cells)>BOMB_START:\n",
    "        b=BOMB_MAX\n",
    "    for i in range(b):   \n",
    "        sx=int(random()*Terrain.shape[0])\n",
    "        sy=int(random()*Terrain.shape[1])\n",
    "        for xi in range(sx,sx+BOMB_SIZE):\n",
    "            for yi in range(sy,sy+BOMB_SIZE):\n",
    "                if (xi,yi) in cells.keys():\n",
    "                    cells[(xi,yi)].destroy()\n",
    "\n",
    "    #  Вывод текстом номера кадра и числа клеток (чтобы следить за процессом при записи анимации в файл)    \n",
    "    \n",
    "    framecounter+=1\n",
    "    clear_output(wait=True)\n",
    "    print(framecounter,len(cells))  \n",
    "    \n",
    "    # Отображение выбранной карты\n",
    "    \n",
    "    if DISPLAY==0:\n",
    "        im1=plt.imshow(ShowTerrain, cmap=plt.get_cmap('bone'))\n",
    "    elif DISPLAY==1:\n",
    "        Zm = np.ma.masked_where(Terrain == 0, Conc)\n",
    "        im1=plt.imshow(Zm, cmap=plt.get_cmap('magma').with_extremes(bad='#101020'))\n",
    "    elif DISPLAY==2:\n",
    "        Zm = np.ma.masked_where(TransportActivity<0.1, Flow)\n",
    "        im1=plt.imshow(Zm, cmap=plt.get_cmap('hsv').with_extremes(bad='k'))\n",
    "    elif DISPLAY==3:\n",
    "        Zm = np.ma.masked_where(TransportActivity<0.1, Dir)\n",
    "        im1=plt.imshow(Zm, cmap=plt.get_cmap('brg').with_extremes(bad='k'))\n",
    "    elif DISPLAY==4:\n",
    "        im1=plt.imshow(Store, cmap=plt.get_cmap('gist_stern'))\n",
    "    elif DISPLAY==5:\n",
    "        Zm = np.ma.masked_where(Terrain == 0, TransportActivity)\n",
    "        im1=plt.imshow(Zm, cmap=plt.get_cmap('hot').with_extremes(bad='#101020'))\n",
    "    return im1,\n",
    "\n",
    "# Настройка анимации\n",
    "ani = animation.FuncAnimation(fig, updatefig, frames=600, interval=3, blit=True)\n",
    "plt.show()\n",
    "#ani.save('im0.mp4', writer=writer)  # Сохранить анимацию в файле"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
