Ця математична модель, винайдена Джоном Конвеем – спроба змоделювати процеси реального життя за допомогою простих правил. Гравці для неї не потрібні, тому після налаштування початкового стани не потрібно ніякого втручання. Подробиці цієї гри можна знайти в Інтернеті і ми настійно рекомендуємо вам почитати про неї. Пропонований проект дозволяє користувачеві змоделювати дану гру на дисплеї Nokia за допомогою мікроконтролера tinyAVR. В оригінальній грі використовується двовимірне поле нескінченного розміру, але в нашому пристрої розмір ігрового простору обмежений 16×16 осередків. Користувач може задати початковий стан за допомогою кнопок (як показано на рис. 4.21). Після налаштування початкового стану гру можна запустити і подивитися на її еволюцію.

Рис. 4.19. Друкована плата іграшки Тенгу (сторона друкованих провідників)

Рис. 4.21. Гра “Життя”

Специфікація проекту

Мета проекту – створити модель гри на мікроконтролері з графічним дисплеєм і дозволити користувачеві встановлювати початковий стан для запуску гри. Включення будь-якого елементу ігрового поля 16×16 здійснюється за допомогою чотирьох кнопок. Схема живиться від батарейок.

Опис пристрою

Рис. 4.22. Зовнішній вигляд гри

Схема пристрою – та ж сама, що і в проекті температурного реєстратора. У тому проекті використовувалася тільки одна з наявних чотирьох кнопок, а тепер будуть задіяні всі кнопки. Призначення перших двох кнопок – переміщення курсору вліво / вправо і вгору / вниз (вони називаються кнопками стрілок). Третя кнопка служить для перемикання стану елемента, а четверта – для запуску гри. Зображення дисплея показано на рис. 4.22.

Програмування

Відкомпільований вихідний код (разом з файлом MAKEFILE) можна завантажити за посиланням: www.avrgenius.com/tinyavrl.

Тактова частота дорівнює 8 МГц. Контролер запрограмований за допомогою STK500 в режимі програмування ISP. Пояснимо найбільш важливі фрагменти коду (лістинг 4.6).

ЛСТІНГ 4д>

void place (void)

(

row=0; column=0; while(1)

{

if (! (PINA&ObO0000001))

{

_delay_ms(30);

while (! (PINA&ObOOOOOOOl));

_delay_ms ‘(30)

TIMSK1 = 0x00;

if (led[Of [row] & (l«column) )

{

pix_light (row, column, 1) ;

}

else if (!(led[0][roW]&(lcccolumn))) {

pix_light(row,column,0);

}

row++;

if (row== (ROWMAX+l) ) row = 0;

TIMSK1 = 0x01;

}

if (‘ (PINA&ObOOOOOOlO))

{

_delay_ms(30);

while (! (PINA&ObOOOOOOlO));

_delay_ms(3 0);

TIMSK1 = 0x00;

if (led[0] [row] & (l«column) )

{ pix_light (row, column, 1) ;

}

else if ( ! (led[0] [row] & (l«column) ) )

{

pix_light (row, column, 0) ;

}

column++;

if(column==(COLMAX+1)) column = 0;

TIMSK1 = 0x01;

}

if (! (PINA&ObO0100000))

{

_delay_ms (50);

while ( ! (PINA&ObOOlOOOOO) ), ;

_delay_ms (50);

TIMSK1 = 0x00; if(led[0] [row]& (lcccolumn)) led[0] [row] &=~ (l«column) ; else led[0] [row] |=l«column; if (led[0] [row] & (l«column) )

{

pix_light (row, column, 1) ;

}

else if ( ! (led[0] [row] & (l«column) ) )

{

pix_light (row, column, 0) ;

}

row++ ;

if (row== (ROWMAX+1) ) row = 0;

TIMSK1 = 0x01;

}

if (! (PINA&ObO0001000))

{

_delay„ms(3 0);

while (! (PINA&ObOOOOlOOO));

_delay_ms (3 0);

TIMSK1 = 0x00;

if (led[0] [row] & (l«column)

{

pix_light (row, column, 1) ;

}

else if (! (led[0] [row] & (l«column))) {

pix_light(row,column,0);

}

TIMSK1 = 0x01; break;

}

}

}

Функція, наведена в лістингу 4.6, служить для установки початкового стану (комбінації “живих” і “мертвих” осередків). Кожна комірка на дисплеї LCD складається з дев’яти пікселів. Верхні ліві 2×2 пікселя у активних осередків світяться, а інші п’ять пікселів завжди вимкнені (щоб уникнути злиття сусідніх пікселів). Всі ігрове поле складається з 16×16 таких осередків. Для оптимального завантаження оперативної пам’яті ми використовували для подання цієї конфігурації цілочисельні масиви (розміром по 16 елементів). Каада змінна масиву відповідає одному рядку (її 16 бітів означають 16 стовпців). Налаштування початкового стану здійснюється чотирма кнопками: дві переміщують курсор по рядках і стовпцях, третя перемикає стан поточної комірки, а остання запускає гру.

while(1)

{

clearгam (); stage = 0;

for(unsigned char j=0;j<=5;j++)

{

cursorxy(48,j); writedata(OxFF); writedata(OxFF); writedata(OxFF);

}

TIMSK1 = 0x01;

// Вектор переривання за переповненням place ();

TIMSK1 = 0x00;

// Відключення переривання за переповненням cursorxy (52,0); put character (‘S’);

cursorxy(52,1); putcharacter(‘T’); cursorxy(52,2); putcharacter(Ά’); cursorxy(52,3); putcharacter( ‘ G’ ) ; cursorxy(52,4) ; putcharacter(‘E’); generation=0; generationl=l; while(1)

{

for (int row=0;row<=ROWMAX;row++)

{

for(int col=0;col<=COLMAX;col++)

{

if (led[generation] [row] & (l«col) ) led[generationl] [row] |=l«col; else led[generationl][row]

&=~ (l«col) ;

if (led [generation] [row] & (l < < col)) // включено

{

check_neighbors(generation,row,col) if(alive>3||alive<2)

‘ {

led [generationl] [row] &= ~(l«col) pix_light(row,col,0);

}

}

else

if ( ! (led[generation] [row] & (l«col) ) )

{

check_neighbors(generation,row,col) if(alive == 3)

{

led [generationl] [row] |= l«col; pix_light(row,col,1);

}

}

}

}

generation = (generation==0)?1:0; generationl = (generation==0)?1:0;

// Правила гри stage ++; stagel = stage;

cursorxy(60,2);putcharacter (stagel/100+48)

stagel = stagel%100;

putcharacter(stagel/10+48);

stagel = stagel%10;

putcharacter(stagel+48);

_delay_ms(5 0 0); flag=0;

for(unsigned char i=0;i<=15;i++)

{

if (! (actual[i]==0))

{

flag=l;

break;

}

}

if ((flag==0) | | (stage==255))

{

clearram() ;

for(unsigned char i=0;l<=15;i++)

{

led[0][i)=0; led[l][i]=0; actual[i]=0; flag=0;

}

cursorxy(12,2); putstr("GAME OVER"); while(PINA&OxOl);

_delay_ms(30); while (! (PINA&OxOl));

_delay_ms(30); break;

}

}

}

Лістинг 4.7 – це головний нескінченний цикл програми. Він виконує дві основні дії: перевіряє стан кожного осередку (відповідно до правил гри) і оновлює дисплей. Одночасно він заповнює наступний стан кожного осередку (у відповідній позиції масиву led). Він перевіряє кількість ” живих “сусідів осередки: більше трьох або менше двох (максимально сусідів може бути вісім). Якщо одна з цих умов вірно, то осередок вимикається (“вмирає”). У тому випадку, коли у осередку рівно три сусіда – вона зберігається (або включається, якщо вона “мертва”). Після перевірки всіх осередків наступний стан стає поточним (і т. д.). Для ітерації між поточним і наступним поколіннями потрібні ТІЛЬКИ дві змінні (generation І generationl). Гра закінчується, коли “вмирають” (Вимикаються) всі осередки або коли значення stage (число “поколінь”) стає дорівнює 255.

Решта частини коду инициализируют різні змінні, функції для сполучення з LCD і процедуру обробки переривання (для переповнення TIMER 1). Процедура обробки переривання вступає в справу тільки при налаштуванні початкового стану (для того, щоб блимала поточна комірка, на яку вказує курсор).

Робота пристрою

Для включення пристрою потрібно подати живлення на плату. З’явиться миготливий курсор в лівому верхньому кутку екрану. За допомогою кнопок вибирайте потрібні елементи і перемикайте їх стан. Коли початковий стан гри буде налаштовано, натисніть кнопку для запуску процесу. Праворуч на дисплеї відображаються ітерації.

Джерело: Гадре, Д., Цікаві проекти на базі мікроконтролерів tinyAVR / Дхананья Гадре, Нігула Мелхотра: Пер. з англ. – СПб .: БХВ-Петербург, 2012. – 352 с .: іл. – (Електроніка)