Cat hungry.png
Здравствуйте! Собираем деньги на перевод материалов по электронике(https://www.allaboutcircuits.com/education/). Реквизиты указаны здесь.

Arduino:Примеры/life

Материал из Онлайн справочника
Перейти к: навигация, поиск

Перевод: Максим Кузьмин (Cubewriter)
Перевел 2686 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Игра «Жизнь»[1]

Этот пример показывает при помощи Arduino и библиотеки GLCD воспроизвести на GLCD-панели клеточный автомат Джона Конуэя (John Conway) под названием «Игра “Жизнь”».

Код

  1.  
  2. /*
  3.  * Игра «Жизнь»
  4.  *
  5.  * Игра «Жизнь» – клеточный автомат, придуманный Джоном Конуэем
  6.  * (John Conway) и адаптированный под Arduino GLCD.
  7.  *
  8.  */
  9.  
  10. #include <glcd.h>
  11. #include "fonts/SystemFont5x7.h"       // системный шрифт
  12.  
  13. /********* пользовательские настройки ************/
  14. #define CELL_SIZE 4        // высота и ширина каждой клетки
  15. #define DELAY    50        // задержка (в миллисекундах) между показом поколений
  16.  
  17. /******* обработка пользовательских настроек ********/
  18. #define ROWS    (DISPLAY_HEIGHT / CELL_SIZE)
  19. #define COLUMNS  (DISPLAY_WIDTH / CELL_SIZE)
  20.  
  21. #define BYTES_PER_COLUMN ((COLUMNS + 7)/ 8)  //  это количество байт, необходимых для хранения столбца
  22.  
  23. #define MAX_ITERATIONS  500  // при достижении этого числа проходы обнуляются
  24. #define STABLE_GENERATIONS 4  // должно быть как минимум «2», это количество старых поколений, которые проверяются для стабильности
  25.  
  26. #define MIN_OBJECTS 2     // минимальное количество объектов, которые будут посеяны случайным генератором
  27. #define MAX_OBJECTS 4     // максимальное количество объектов, которые будут посеяны случайным генератором
  28.  
  29. #define MARGIN 4          // минимальная дистанция от края экрана для случайно посеянных объектов
  30.  
  31.  
  32. #define BITVAL(_pos) (1<< (7-(_pos % 8)))  // макрос для получения значения «bitfield»
  33.  
  34. #define ALIVE true
  35. #define DEAD false
  36.  
  37. /* ========   Объекты, участвующие в «Игре жизни»   ========
  38.  * Это объекты, которые будут участвовать в «Игре жизни»
  39.  * (или, другими словами, которые могут быть отображаться на дисплее).
  40.  * Чтобы добавить свой объект, создайте и инициализируйте структуру,
  41.  * следуя примерам ниже.
  42.  * Добавьте в таблицу объектов обращение к своей структуре,
  43.  * а также назовите свою структуру, добавив ее в список «enum».
  44.  */
  45.  
  46. // шаблон структуры для живых объектов:
  47. typedef struct {
  48.          byte height ;
  49.          byte width ;  
  50.          byte bitfield[];
  51.      } object_t, *object_ptr_t;
  52.  
  53. // ниже создаются и инициализируются все объекты, которые будут
  54. // участвовать в «Игре жизни»:
  55. struct {
  56.          byte height ;
  57.          byte width ;
  58.          byte bitfield[2];
  59.        } table = {2,4,  { 0b10010000 ,       //  *  *  
  60.                           0b11110000 } };    //  ****
  61.                          
  62. struct {
  63.          byte height ;
  64.          byte width ;  
  65.          byte bitfield[3];
  66.        } glider  = {3,3, { 0b00100000 ,      //    *    
  67.                            0b10100000 ,      //  * *
  68.                            0b01100000 }};    //   **
  69.                          
  70. struct {
  71.          byte height ;
  72.          byte width ;  
  73.          byte bitfield[3];
  74.        } glider2 = {3,3, { 0b10100000 ,      //  * *
  75.                            0b11000000 ,      //  **
  76.                            0b11110000 }};    //  ****                      
  77. struct {
  78.          byte height ;
  79.          byte width ;  
  80.          byte bitfield[4];
  81.        } loaf = {4,4,    { 0b01100000 ,     //   **
  82.                            0b10010000 ,     //  *  *  
  83.                            0b01010000 ,     //   * *
  84.                            0b00100000 }};   //    *                      
  85. struct {
  86.          byte height ;
  87.          byte width ;  
  88.          byte bitfield[3];
  89.        } ship = {3,3,    { 0b11000000 ,     //   **
  90.                            0b10100000 ,     //   * *  
  91.                            0b01100000 }};   //    **
  92.  
  93. struct {
  94.          byte height ;
  95.          byte width ;  
  96.          byte bitfield[4];
  97.        } behive = {4,3,  { 0b01000000 ,     //   *    
  98.                            0b10100000 ,     //  * *
  99.                            0b10100000 ,     //  * *
  100.                            0b01000000}};    //   *
  101.                            
  102. struct {
  103.          byte height ;
  104.          byte width ;  
  105.          byte bitfield[1];
  106.        } blinker = {1,3, { 0b11100000 }};   //  ***
  107.                          
  108.                          
  109. struct {
  110.          byte height ;
  111.          byte width ;  
  112.          byte bitfield[2];
  113.        } block = {2,2, { 0b11000000 ,      //  **
  114.                          0b11000000}};     //  **                    
  115.  
  116. // помещаем все эти объекты в таблицу объектов:                        
  117.  object_ptr_t objectTable[] = {
  118.              (object_ptr_t)&table,
  119.              (object_ptr_t)&glider,
  120.              (object_ptr_t)&loaf,
  121.              (object_ptr_t)&ship,
  122.              (object_ptr_t)&behive,
  123.              (object_ptr_t)&blinker,
  124.              (object_ptr_t)&block
  125.  };  
  126.  
  127. #define OBJECT_COUNT (sizeof( objectTable/ sizeof(object_ptr_t)))
  128.  
  129. // Составляем список объектов. Новые объекты добавляем в конец списка.
  130. // Код «Игры жизни» будет обращаться к объектам через названия в этом списке.
  131.  enum  {Table,Glider,Loaf,Ship,Behive,Blinker,Block};
  132.  
  133. byte generations[STABLE_GENERATIONS][ROWS][BYTES_PER_COLUMN]; // массив, используемый для вычисления, рисования и проверки, изменилось ли количество проходов
  134.  
  135. void setup(){    
  136.   GLCD.Init();   // инициализируем библиотеку в режиме рисования
  137.   GLCD.SelectFont(System5x7);
  138.   seed(0);
  139. }
  140.  
  141. void loop (void){    
  142.   GLCD.ClearScreen();
  143.   unsigned int i = generate();  
  144.   GLCD.CursorTo(0,0);
  145.   GLCD.print(i);
  146.   GLCD.print(" iterations");  
  147.   delay(1500);  
  148.   seed(1); // используем случайное семя
  149. }  
  150.  
  151. unsigned int generate(){
  152.   unsigned int iteration = 0;
  153.   byte thisGeneration,nextGeneration, previousGeneration;  
  154.   thisGeneration = nextGeneration  = 0;
  155.   // показываем начальный массив:
  156.   for(int row = 0; row < ROWS; row++)
  157.   {
  158.       for(int column = 0; column < COLUMNS; column++)
  159.       {
  160.         if(isAlive(thisGeneration,row,column))    
  161.            GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE,  CELL_SIZE-1, CELL_SIZE-1, CELL_SIZE/2, BLACK) ;
  162.         else
  163.            GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE,  CELL_SIZE-1, CELL_SIZE-1, CELL_SIZE/2, WHITE) ;
  164.       }
  165.   }
  166.  
  167.   delay(1500); // задержка для показа условий посева
  168.   do{    
  169.     thisGeneration  = iteration % STABLE_GENERATIONS;
  170.     nextGeneration =  (thisGeneration+1) % STABLE_GENERATIONS;
  171.     previousGeneration  =  (thisGeneration-1) % STABLE_GENERATIONS;
  172.     delay(DELAY);    
  173.     memset(generations[nextGeneration],0,sizeof(generations[0])); // очищаем массив для следующего поколения
  174.     // смотрим, кто жив, а кто умер:
  175.     for(int row = 0; row < ROWS; row++){      
  176.       for(int column = 0; column < COLUMNS; column++){
  177.         boolean cell =  isAlive(thisGeneration,row,column);
  178.         byte n = getNeighborCount(thisGeneration,row,column);
  179.         if(cell == DEAD){
  180.           if(n==3) {
  181.             setLife(nextGeneration,row,column,ALIVE); // рождение    
  182.             GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE,  CELL_SIZE-1, CELL_SIZE-1, CELL_SIZE/2, BLACK) ;
  183.           }
  184.         }
  185.         else{
  186.           if(n==2 || n==3) {
  187.             setLife(nextGeneration,row,column,ALIVE);  // выживание
  188.             // рисовать клетку не нужно, поскольку она уже есть:
  189.           } else {
  190.             setLife(nextGeneration,row,column,DEAD); // смерть
  191.             GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE,  CELL_SIZE-1, CELL_SIZE-1, CELL_SIZE/2, WHITE) ;
  192.           }
  193.         }
  194.       }
  195.     }
  196.   }
  197.   while(isStable(thisGeneration) == false  && ++iteration < MAX_ITERATIONS ) ;
  198.   return iteration;
  199. }
  200.  
  201. void seed(int seedValue){
  202.   // если seedvalue равно «0», делаем запрограммированный посев; во всех остальных случаях делаем случайный посев
  203.   memset(generations,0,sizeof(generations)); // очищаем все массивы для поколений
  204.   if(seedValue == 0){
  205.     // размещаем объекты по умолчанию; аргумента – это смещение от центра
  206.     loadObject(Table,2,-9);    // размещаем объект «table» в двух рядах ниже и девяти столбцах левее центра
  207.     loadObject(Glider, -5,-9);   // размещаем объект «glider» в пяти рядах выше и в девяти столбцах левее центра
  208.   }
  209.   else{  
  210.   // загружаем несколько случайных объектов и размещаем в случайных позициях:
  211.      byte nbrObjects = random(MIN_OBJECTS,MAX_OBJECTS+1);  // создаем случайное количество объектов
  212.      for(byte i=0; i < nbrObjects;i++){
  213.         byte obj = random(7); // OBJECT_COUNT);
  214.         int column = random(MARGIN - (COLUMNS/2), (COLUMNS/2) - MARGIN);
  215.         int row = random(MARGIN - (ROWS/2)   , (ROWS/2) - MARGIN);
  216.         loadObject(obj,row,column);
  217.      }  
  218.   }  
  219. }
  220.  
  221. void loadObject(void * object, char y, char x){
  222.   // object_ptr – это загружаемый объект;
  223.   // X и Y – это координаты смещения от центра дисплея:
  224.   object_ptr_t object_ptr = (object_ptr_t)object;
  225.   byte row = (ROWS/2) + y;
  226.   byte column = (COLUMNS/2) + x;  
  227.   for(byte ys = 0; ys < object_ptr->height; ys++ ){
  228.     for(byte xs=0; xs < object_ptr->width; xs++ ){
  229.       if( (row +ys < ROWS) && (column +xs < COLUMNS)){
  230.         // этот код работает, только если в «width» содержится до 8 бит:
  231.         boolean value = object_ptr->bitfield[ys] & BITVAL(xs) ;  
  232.         setLife(0, row+ys, column+xs, value) ; // объекты всегда загружаются в первый массив
  233.       }  
  234.     }          
  235.   }      
  236. }
  237.  
  238. void loadObject(int object, char y, char x){
  239.   loadObject(objectTable[object], y, x );
  240. }
  241.  
  242. boolean isStable(byte thisGeneration){
  243.   // возвращает «true», если два зарегистрированных поколения – одинаковые:
  244.   for(byte i=0; i < STABLE_GENERATIONS; i++)
  245.     if(i != thisGeneration)
  246.       if(memcmp(generations[thisGeneration], generations[i], sizeof(generations[0])) == 0)
  247.         return true;
  248.   return false;        
  249. }
  250.  
  251. boolean isAlive( byte generation, byte row, byte column){
  252.   byte  b = generations[generation][row][column /8];
  253.   return b &  BITVAL(column) ;  
  254. }
  255.  
  256. void setLife( byte generation, byte row, byte column, boolean state){
  257.   byte elem = column /8;  
  258.   byte  b = generations[generation][row][elem];
  259.   if(state != DEAD){
  260.     b = b | BITVAL(column);
  261.   }
  262.   else{
  263.     b &= ~(BITVAL(column)) ;  
  264.   }
  265.   generations[generation][row][elem] = b;
  266. }
  267.  
  268. byte getNeighborCount( byte generation, byte row, byte column){
  269.   byte count = 0;
  270.   for(byte d = 0;d < 8; d++)
  271.     if(isNeighborAlive(generation,row,column,d) != 0)
  272.       count++;
  273.   return count;
  274. }
  275.  
  276. boolean isNeighborAlive( byte generation , byte row, byte column, byte dir){
  277.   byte nrow=row;
  278.   byte ncol=column;
  279.   if(dir == 7 || dir == 0 || dir == 1)
  280.     nrow--;
  281.   if(dir == 3 || dir == 4 || dir == 5)
  282.     nrow++;
  283.   if(dir == 5 || dir == 6 || dir == 7)
  284.     ncol--;
  285.   if(dir == 1 || dir == 2 || dir == 3)
  286.     ncol++;
  287.   if(ncol==255) ncol = COLUMNS - 1;
  288.   if(ncol==COLUMNS) ncol = 0;
  289.   if(nrow==255) nrow = ROWS - 1;
  290.   if(nrow==ROWS) nrow = 0;
  291.   return isAlive(generation,nrow,ncol);
  292. }

См.также

Внешние ссылки

  1. glcd-v3-20111205.zip