Arduino:Примеры/life

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

Перевод: Максим Кузьмин (Cubewriter) Перевел 364226 статей для сайта.</br>Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Игра «Жизнь»[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 }

См.также

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