This shows you the differences between two versions of the page.
pm:prj2024:vstoica:gheorghe.grosu [2024/05/21 23:34] gheorghe.grosu [Software Design] |
pm:prj2024:vstoica:gheorghe.grosu [2024/05/26 18:13] (current) gheorghe.grosu [Jurnal] |
||
---|---|---|---|
Line 49: | Line 49: | ||
{{:pm:prj2024:vstoica:snake_2024_schem_v4.png?600|}} | {{:pm:prj2024:vstoica:snake_2024_schem_v4.png?600|}} | ||
+ | |||
+ | {{:pm:prj2024:vstoica:img_3423.jpeg?600|}} | ||
Line 57: | Line 59: | ||
1. Mediu de dezvoltare: | 1. Mediu de dezvoltare: | ||
- **Platformă de dezvoltare**: Arduino IDE | - **Platformă de dezvoltare**: Arduino IDE | ||
- | - Arduino IDE este utilizat pentru scrierea, compilarea și încărcarea firmware-ului pe un microcontroller Arduino. | + | Arduino IDE este utilizat pentru scrierea, compilarea și încărcarea firmware-ului pe un microcontroller Arduino. |
2. Librării şi surse 3rd-party: | 2. Librării şi surse 3rd-party: | ||
Line 70: | Line 72: | ||
- Functia `tone()` este utilizată pentru a genera sunete pe pinul difuzorului. | - Functia `tone()` este utilizată pentru a genera sunete pe pinul difuzorului. | ||
- | #### 3. Algoritmi şi structuri implementate: | + | 3. Algoritmi şi structuri implementate: |
- **Structuri de date**: | - **Structuri de date**: | ||
- | - `Joystick`: Monitorizează direcția joystick-ului pentru controlul șarpelui. | + | |
- | - `GameState`: Gestionează stările jocului și timpul de cadru pentru fiecare stare. | + | **`Joystick`** |
- | - `Snake`: Păstrează lungimea și pozițiile șarpelui. | + | Structura `Joystick` se ocupă de citirea pozițiilor analogice ale joystick-ului și de calcularea direcțiilor următoare pentru deplasarea șarpelui. |
+ | |||
+ | **`GameState`** | ||
+ | |||
+ | Structura `GameState` gestionează stările jocului și timpul de cadru pentru fiecare stare, facilitând tranzițiile între stări și verificarea momentului potrivit pentru actualizarea jocului. | ||
+ | |||
+ | **`Snake` | ||
+ | ** | ||
+ | |||
+ | Structura `Snake` gestionează pozițiile șarpelui, lungimea sa și funcțiile pentru resetare, mișcare și creștere. De asemenea, verifică dacă șarpele a colizionat cu el însuși. | ||
+ | |||
+ | Șarpele este reprezentat prin structura `Snake`, care conține: | ||
+ | * **`length`**: lungimea șarpelui. | ||
+ | * **`positions`**: array de tip `byte` ce stochează pozițiile fiecărui segment al șarpelui. | ||
+ | |||
+ | Motive pentru această reprezentare: | ||
+ | |||
+ | 1. **Eficiență Memorie**: Folosirea `byte` ocupă doar 1 octet, economisind memorie RAM limitată pe Arduino. | ||
+ | |||
+ | 2. **Simplitate și Performanță**: Acces rapid și simplu la pozițiile șarpelui pentru operațiuni de mișcare și creștere. | ||
+ | |||
+ | 3. **Gestionare Ușoară a Lungimii**: Lungimea și pozițiile sunt ușor de manipulat pentru actualizări rapide în timpul jocului. | ||
- **Algoritmi**: | - **Algoritmi**: | ||
- | - **Controlul jocului**: Monitorizează intrările de la joystick pentru a controla direcția șarpelui și pentru a trece între diferitele stări ale jocului. | + | - Controlul direcției șarpelui pe baza intrărilor de la joystick. |
- | - **Detectarea coliziunilor**: Verifică coliziunile șarpelui cu obstacolele, pereții și corpul propriu. | + | - Verificarea coliziunilor cu pereții, obstacolele și corpul șarpelui. |
- | - **Generarea aleatorie a poziției merelor**: Utilizează funcția `random()` pentru a genera poziții aleatorii pentru mere. | + | - Gestionarea creșterii șarpelui și actualizarea scorului. |
+ | - Generarea aleatorie a pozițiilor merelor. | ||
+ | - Stocarea și citirea scorurilor maxime din EEPROM. | ||
- | #### 4. Surse şi funcţii implementate: | + | Funcțiile implementate |
- | - **Setup-ul inițial**: | + | |
- | ```cpp | + | |
- | void setup() { | + | |
- | Serial.begin(9600); | + | |
- | randomSeed(analogRead(5)); | + | |
- | pinMode(SW, INPUT_PULLUP); | + | |
- | pinMode(PIN_RED, OUTPUT); | + | |
- | pinMode(PIN_GREEN, OUTPUT); | + | |
- | tft.initR(INITR_BLACKTAB); | + | |
- | level_select_init(); | + | |
- | last_frame_time = millis(); | + | |
- | curr_note_start_time = 0; | + | |
- | } | + | |
- | ``` | + | |
- | - Initializează serialul pentru debugging. | + | |
- | - Configurează pinurile pentru joystick, LED-uri și difuzor. | + | |
- | - Inițializează ecranul TFT și structurile de date. | + | |
- | - **Starea de selecție a nivelului**: | + | Funcții principale: |
- | ```cpp | + | |
- | void state_level_select() { | + | |
- | if (joystick.dir_y == 1) { | + | |
- | selected_level = (selected_level + 1) % 3; | + | |
- | } else if (joystick.dir_y == -1) { | + | |
- | selected_level = (selected_level - 1); | + | |
- | if (selected_level < 0) selected_level = 2; | + | |
- | } | + | |
- | tft.fillRect(64, 40, 20, 60, ST7735_BLACK); | + | |
- | tft.fillTriangle(80, 44 + 18 * selected_level, 80, 60 + 18 * selected_level, 64, 52 + 18 * selected_level, ST7735_WHITE); | + | |
- | if (digitalRead(SW) == LOW) { | + | |
- | gameState.transition_to(LEVEL_INIT, LEVEL_INIT_FRAME_TIME); | + | |
- | } | + | |
- | } | + | |
- | ``` | + | |
- | - Permite utilizatorului să selecteze nivelul dorit utilizând joystick-ul și butonul. | + | |
- | - **Inițializarea nivelului**: | + | * **setup()** |
- | ```cpp | + | - Inițializează comunicarea serială. |
- | void state_level_init() { | + | - Configurează pinii pentru joystick și LED-urile RGB. |
- | tft.fillScreen(ST7735_BLACK); | + | - Inițializează ecranul TFT. |
- | score = 0; | + | - Afișează ecranul de selecție a nivelului. |
- | max_score = 100; | + | - Inițializează starea și timpul de cadru pentru joc. |
- | snake.reset(); | + | |
- | empty_spaces = N_TILES * M_TILES - snake.length; | + | |
- | memset(tiles, 0, sizeof(tiles)); | + | |
- | if (selected_level >= 1) { | + | |
- | for (int i = 0; i < N_TILES; i++) { | + | |
- | renderIfDifferent(i, OBSTACLE); | + | |
- | renderIfDifferent(N_TILES * M_TILES - 1 - i, OBSTACLE); | + | |
- | renderIfDifferent(i * M_TILES, OBSTACLE); | + | |
- | renderIfDifferent(i * M_TILES + M_TILES - 1, OBSTACLE); | + | |
- | } | + | |
- | empty_spaces -= N_TILES * 4; | + | |
- | } | + | |
- | if (selected_level == 2) { | + | |
- | for (int i = 0; i < sizeof(level3_obstacles); i++) { | + | |
- | int col = level3_obstacles[i] % M_TILES; | + | |
- | int row = level3_obstacles[i] / N_TILES; | + | |
- | for (int j = -1; j <= 1; j++) { | + | |
- | renderIfDifferent((row + j) * M_TILES + col, OBSTACLE); | + | |
- | } | + | |
- | for (int k = -1; k <= 1; k++) { | + | |
- | renderIfDifferent(row * M_TILES + col + k, OBSTACLE); | + | |
- | } | + | |
- | empty_spaces -= 3; | + | |
- | } | + | |
- | } | + | |
- | if (random(0, 10) == 0) { | + | |
- | renderIfDifferent(get_random_pos(), RED_APPLE); | + | |
- | } else { | + | |
- | renderIfDifferent(get_random_pos(), APPLE); | + | |
- | } | + | |
- | renderIfDifferent(snake.positions[snake.length - 1], SNAKE_HEAD); | + | |
- | for (int i = 0; i < snake.length - 1; i++) { | + | |
- | renderIfDifferent(snake.positions[i], SNAKE_BODY); | + | |
- | } | + | |
- | eeprom_position = selected_level * 4; | + | |
- | EEPROM.get(eeprom_position, high_score); | + | |
- | gameState.transition_to(LEVEL_RUNNING, LEVEL_RUNNING_FRAME_TIME); | + | |
- | } | + | |
- | ``` | + | |
- | - Configurează elementele grafică și obstacolele pentru nivelul selectat. | + | |
- | - Inițializează pozițiile șarpelui și merele pe ecran. | + | |
- | - **Rularea nivelului**: | + | * **loop()** |
- | ```cpp | + | - Verifică dacă trebuie să actualizeze starea jocului pe baza timpului de cadru. |
- | void state_level_running() { | + | - Citește direcțiile de la joystick. |
- | int new_head_x = joystick.next_x(get_x(snake.positions[snake.length - 1])); | + | - Execută funcțiile corespunzătoare fiecărei stări (selecție nivel, inițializare nivel, rulare nivel, game over). |
- | int new_head_y = joystick.next_y(get_y(snake.positions[snake.length - 1])); | + | |
- | byte next_pos = new_head_y * M_TILES + new_head_x; | + | |
- | if (snake.has_collided_with_self(next_pos)) { | + | |
- | gameState.transition_to(GAME_OVER, GAME_OVER_FRAME_TIME); | + | |
- | return; | + | |
- | } | + | |
- | if (score != max_score) { | + | |
- | score += 10; | + | |
- | if (score < 500) { | + | |
- | analogWrite(PIN_GREEN, score / 4); | + | |
- | } | + | |
- | } | + | |
- | if (tiles[next_pos] == APPLE || tiles[next_pos] == RED_APPLE) { | + | |
- | renderIfDifferent(snake.positions[snake.length - 1], SNAKE_BODY); | + | |
- | snake.grow(next_pos); | + | |
- | renderIfDifferent(snake.positions[snake.length - 1], SNAKE_HEAD); | + | |
- | empty_spaces--; | + | |
- | renderIfDifferent(get_random_pos(), APPLE); | + | |
- | if (tiles[next_pos] == RED_APPLE) { | + | |
- | max_score += 100; | + | |
- | } else { | + | |
- | max_score += 50; | + | |
- | } | + | |
- | tone(SPEAKER_PIN, APPLE_EAT_TONE, APPLE_EAT_DURATION); | + | |
- | return; | + | |
- | } | + | |
- | if (tiles[next_pos] == OBSTACLE) { | + | |
- | gameState.transition_to(GAME_OVER, GAME_OVER_FRAME_TIME); | + | |
- | } | + | |
- | renderIfDifferent(snake.positions[0], GRASS); | + | |
- | renderIfDifferent(snake.positions[snake.length - 1], SNAKE_BODY); | + | |
- | snake.move(next_pos); | + | |
- | renderIfDifferent(snake.positions[snake.length - 1], SNAKE_HEAD); | + | |
- | } | + | |
- | ``` | + | |
- | - Monitorizează și actualizează pozițiile șarpelui în funcție de intrările de la joystick. | + | |
- | - Verifică coliziunile și actualizează scorul în funcție de interacțiunea cu merele. | + | |
- | - **Finalul jocului**: | + | **Funcții de stare:** |
- | ```cpp | + | |
- | void state_game_over() { | + | |
- | if (score > high_score) { | + | |
- | high_score = score; | + | |
- | EEPROM.put(eeprom_position, high_score); | + | |
- | } | + | |
- | digitalWrite(PIN_RED, HIGH); | + | |
- | tft.fillScreen(ST7735_RED); | + | |
- | tft.setCursor(15, 60); | + | |
- | tft.setTextSize(2); | + | |
- | tft.println("YOU LOST"); | + | |
- | tft.setTextSize(1); | + | |
- | tft.setCursor(10, 90); | + | |
- | tft.println("Your points " + String(score)); | + | |
- | tft.setCursor(10, 110); | + | |
- | tft.println("Best score " + String(high_score)); | + | |
- | } | + | |
- | ``` | + | |
- | - Afișează mesajul de finalizare a jocului și scorurile. | + | |
- | - Salvează scorurile maxime în EEPROM pentru a le menține între sesiunile de joc. | + | |
- | - **Ciclu principal**: | + | * **state_level_select()** |
- | ```cpp | + | - Permite utilizatorului să aleagă nivelul dorit folosind joystick-ul. |
- | void loop() { | + | - Afișează săgeata de selecție pe ecran. |
- | if (gameState.should_update()) { | + | - Trecerea la starea de inițializare a nivelului atunci când utilizatorul apasă butonul de selecție. |
- | gameState.update_time(); | + | |
- | joystick.set_dirs(); | + | |
- | switch (gameState.state) { | + | |
- | case LEVEL_SELECT: state_level_select(); break; | + | |
- | case LEVEL_INIT: state_level_init(); break; | + | |
- | case LEVEL_RUNNING: state_level_running(); break; | + | |
- | case GAME_OVER: state_game_over(); break; | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | ``` | + | |
- | - Actualizează starea jocului și procesează intrările utilizatorului și logica jocului în funcție de timpul de cadru. | + | |
- | ### Concluzie | + | * **state_level_init()** |
- | Acest design modular | + | - Inițializează nivelul selectat, resetând șarpele și plasând obstacole și mere pe hartă. |
- | ===== Rezultate Obţinute ===== | + | - Trecerea la starea de rulare a nivelului. |
- | <note tip> | + | * **state_level_running()** |
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | - Actualizează poziția șarpelui pe baza direcțiilor de la joystick. |
- | </note> | + | - Verifică coliziunile și gestionează creșterea șarpelui. |
+ | - Actualizează scorul și generează sunete atunci când șarpele mănâncă un măr. | ||
+ | - Trecerea la starea de game over în caz de coliziune. | ||
+ | |||
+ | * **state_game_over()** | ||
+ | - Afișează scorul final și scorul maxim pe ecran. | ||
+ | - Salvează scorul maxim în EEPROM dacă este cazul. | ||
+ | - Afișează un mesaj de finalizare a jocului. | ||
+ | |||
+ | **Funcții utilitare:** | ||
+ | |||
+ | 1. **level_select_init()** | ||
+ | * Afișează ecranul de selecție a nivelului. | ||
+ | |||
+ | 2. **renderIfDifferent(int pos, int tile)** | ||
+ | * Redă graficul corespunzător pentru un tile dacă este diferit de cel precedent. | ||
+ | |||
+ | 3. **render_grass(int pos_x, int pos_y)** | ||
+ | * Redă grafica pentru iarbă. | ||
+ | |||
+ | 4. **render_snake_head(int pos_x, int pos_y)** | ||
+ | * Redă grafica pentru capul șarpelui. | ||
+ | |||
+ | 5. **render_snake_body(int pos_x, int pos_y)** | ||
+ | * Redă grafica pentru corpul șarpelui. | ||
+ | |||
+ | 6. **render_apple(int pos_x, int pos_y)** | ||
+ | * Redă grafica pentru un măr. | ||
+ | |||
+ | 7. **render_red_apple(int pos_x, int pos_y)** | ||
+ | * Redă grafica pentru un măr roșu. | ||
+ | |||
+ | 8. **render_obstacle(int pos_x, int pos_y)** | ||
+ | * Redă grafica pentru un obstacol. | ||
+ | |||
+ | 9. **get_random_pos()** | ||
+ | * Generează o poziție aleatorie pentru plasarea unui măr pe hartă. | ||
+ | |||
+ | ===== Rezultate Obţinute ===== | ||
+ | {{:pm:prj2024:vstoica:snk3_gif.gif?250|}} {{:pm:prj2024:vstoica:snk2_gif.gif?250|}}{{:pm:prj2024:vstoica:snk_gif.gif?250|}} | ||
===== Concluzii ===== | ===== Concluzii ===== | ||
Line 260: | Line 182: | ||
<note warning> | <note warning> | ||
- | O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului: surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-). | ||
- | Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**. Namespace-ul în care se încarcă fişierele este de tipul **:pm:prj20??:c?** sau **:pm:prj20??:c?:nume_student** (dacă este cazul). **Exemplu:** Dumitru Alin, 331CC -> **:pm:prj2009:cc:dumitru_alin**. | + | |
+ | {{:pm:prj2024:vstoica:snake.zip|}} | ||
</note> | </note> | ||
Line 272: | Line 195: | ||
* 02/05/2024 - Crearea paginii proiectului si completarea partiala a acesteia | * 02/05/2024 - Crearea paginii proiectului si completarea partiala a acesteia | ||
* 12/05/2024 - Adaugarea schemei electrice si a schemei hardware | * 12/05/2024 - Adaugarea schemei electrice si a schemei hardware | ||
+ | * 22/05/2024 - Adaugarea software designului | ||
+ | * 24/05/2024 - Finisare proiect | ||
</note> | </note> | ||