Tibuleac Andrei
343C4
In cadrul proiectului am implementat jocurile clasice snake si tetris folosind microcontroller-ul ATMega16 si un ecran LCD de Nokia3310
Componente aditionale placii de baza: - ecran LCD Nokia3310 - condensator 10uF - 4 butoane push
Schema legaturii ecran - microcontroller - butoane in ISIS
Pinii folositi pentru comunicare ecran - microcontroller:
Pinii folositi pentru butoane (snake/tetris: pin)
Am folosit libraria pcd8544 pentru interfatarea cu ecranul LCD Mediul de dezvoltare folosit: Programmer's Notepad
Pozitiile segmentelor sarpelui si bucatile de hrana sunt retinute intr-o structura de tip:
struct coord {
byte x, y;
};
Segmentele sarpelui sunt tinute intr-un vector de astfel de structuri.
Functii:
int check_food_pos()
: returneaza 1 daca pozitia hranei nu se intersecteaza cu unul din segmentele sarpelui, altfel returneaza 0.
void generate_food()
: genereaza aleator o pozitie noua pentru hrana, pana cand aceasta este intr-o pozitie acceptata de functia check_food_pos()
.
void checButton(int *direction)
: scrie in direction
directia corespunzatoare butonului apasat.
void initSnake()
: initializeaza sarpele cu pozitia si dimensiunea initiale.
int checkSnake()
: verifica daca sarpele a lovit un zid sau s-a lovit de propriul corp; mai verifica, de asemenea, daca sarpele a ajuns la mancare, caz in care dimensiunea acestuia creste si se semnaleaza faptul ca trebuie generata o nou segment hrana
void showSnake()
: afiseaza pe ecran segmentele sarpelui si hrana
void clearSnake()
: sterge de pe ecran segmentele sarpelui si hrana
void moveSnake()
muta fiecare segment al sarpelui; capul este mutat conform directiei curente a sarpelui, iar fiecare alt segment se muta pe pozitia celui din fata sa
int checkDirection(int old, int current)
: este apelata dupa apasarea unui buton; verifica daca sarpele poate merge in directia indicata; daca old si current au aceeasi directie sau directii opuse, directia curenta ramane neschimbata
Algoritmul principal (se executa cat timp flag-ul dead
nu este 1):
checkButton()
checkDirection()
moveSnake()
pentru a muta sarpele in directia curentacheckSnake()
dead
checkSnake()
sarpele a consumat hrana, se genereaza o noua pozitie corespunzatoare pentru hrana
Pentru piesa in miscare am definit structura:
typedef struct piece
{
sbyte x, y;
sbyte left_clearance, right_clearance;
piece_type type;
int segment_offset_x[3];
int segment_offset_y[3];
}piece_t;
sbyte - tip de date signed char
x, y
→ pozitia unui segment al piesei (care va fi considerat segmentul principal al piesei)
left_clearance, right_clearance
→ distanta dintre segmentul principal si cel mai din stanga, respectiv dreapta, segment al piesei (initial le foloseam pentru a pastra piesa in limitele tablei de joc, dar nu le-am mai folosit, optand pentru alta metoda)
type
: tipul piesei; in total 19 tipuri: cele 7 piese si rotatiile lor (4 rotatii pentru piesele de tip L, J si T; 2 rotatii pentru piesele de tip I, S si Z; o rotatioe pentru piesa de tip O)
segment_offset_x/y[3]
: vectori de 3 elemente ce reprezinta offset-urile pe axa x, respectiv y, ale celorlalte 3 segmente ale piesei fata de segmentul principla
Pentru a retine segmentele deja existente in joc (cele care nu apartin piesei curente in miscare), am folosit o tabela de 25 de elemente (cate unul pentru fiecare rand) de tip short int. Coloanele sunt reprezentate de bitii 0:9 ai acestor elemente.
* initial folosisem o matrice 25×10 de elemente de tip byte, dar am avut bug-uri ciudate
void set_piece_offsets(piece_t *p, piece_type t)
: functie ce seteaza offset-urile segmentelor in functie de fiecare tip de piesa in parte
void draw_square3x3(sbyte x, sbyte y)
: functie ce deseneaza un patrat 3×3 pe ecran cu coltul stanga sus in pozitia data de x si y;
void draw_border()
: deseneaza cele doua linii ce delimiteaza zona de joc
void draw_piece(piece_t *p)
: deseneaza piesa data ca parametru; se deseneaza fiecare segment in parte, folosing functia draw_square3x3()
void init_table()
: initializeaza elementele tabelei de joc cu 0 (oarecum redundant, deoarece tabela este declarata global, dar am vrut sa fiu sigur)
void draw_table()
: deseneaza segmentele din tabela (conform bitilor din fiecare element al vectorului corespunzator tabelei)
sbyte check_position(piece_t *p)
: verifica daca piesa data ca parametru se afla intr-o pozitie valida (nici un segment nu este in afara limitelor tabelei si nici un segment nu se intersecteaza cu alt segment din tabela)
void try_rotate(piece_t *p, piece_type rotation)
: incearca sa transforme piesa primita ca parametru in tipul rotation
(ales in asa fel incat sa fie aceeasi piesa dar cu alta rotatie)
- creeaza o piesa noua cu aceeasi parametrii ca si p
- atribuie piesei noi tipul rotation
- se verifica piesa cu functia check_position()
- daca piesa noua este intr-o pozitie valida, o inlocuieste pe cea veche
- altfel, se repeta procedura pe aceeasi piesa cu pozitia shiftata pe orizontala la stanga si la dreapta (am ales sa fac aceasta verificare pentru cazurile cand se incearca rotatia cand o piesa este lipita de un perete)
void rotate(piece_t *p)
: apeleaza try_rotation()
cu parametrul rotation
ales corespunzator tipului piesei p
sbyte can_advance(piece_t *p)
: verifica daca piesa mai poate avansa, asemanator cu try_rotate:
- creeaza o piesa noua cu aceeasi parametrii ca si p
- decrementeaza atributul y
al piesei noi
- verifica piesa cu check_position()
- daca piesa noua este intr-o pozitie valida, o inlocuieste pe cea veche si returneaza 1
- altfel, returneaza 0
void try_move(piece_t *p, sbyte offset)
: incearca deplasarea piesei pe orizontala cu offset
(care poate fi 1 sau -1); asemanator cu functia can_advance
void copy_to_table(piece_t *p)
: copiaza segmentele piesei trimise ca parametru in tabela jocului
void check_lines()
: foloseste o variabila globala, marked_lines
, pentru a marca liniile pline (cu cate un patrat pe fiecare pozitie)
- variabila este initializata cu 0
- se verifica fiecare element din tabela daca este o linie completa (daca este egal cu 0x03FF: 10 biti de 1); daca da, bitul corespunzator liniei in marked_lines
este setat pe 1
void remove_lines()
: foloseste variabila globala marked_lines
pentru a elimina liniile pline;
Cat timp nu s-a terminat jocul:
- daca nu exista o piesa curenta in joc, piesa curenta devine piesa next
, iar piesa next
este generata aleator (piesa next
este cea afisata in dreapta zonei de joc)
* se verifica piesa noua cu functia can_advance()
, daca esueaza testul, inseamna ca nu poate fi generata (s-a umplut ecranul pana la varf) iar jocul se termina
- in intervalul de timp dintre o avansare in jos a piesei, se trateaza eventualele apasari de buton (miscare pe orizontala, rotatie, coborare rapida)
- se verifica folosind functia can_advance()
daca piesa se poate misca pe verticala in jos; daca nu, aceasta este copiata in tabela de joc cu copy_to_table()
si se eventualele linii pline cu functiile check_lines()
si remove_lines()
Am realizat cu succes atat partea hardware, cat si ambele jocuri.
Pentru codul sursa pentru partea de Snake am pornit de la codul din proiectul lui Vlad Berteanu din 2010 caruia i-am adus cateva modificari.
Schema ISIS este, de asemenea, preluata din acelasi proiect.
Codul sursa si schema ISIS: