Wordle

Nume: Marian Melania-Valentina

Grupă: 331CC

Introducere

Proiectul este bazat pe faimosul joc Wordle. Ideea jocului este următoarea: utilizatorul are 6 încercări pentru a ghici un cuvânt în limba engleză, format din 5 litere - în urma fiecărui cuvânt introdus, utilizatorul primește indicii legate de literele ce aparțin cuvântului, prin colorarea căsuței corespunzătoare fiecărei litere astfel:

  • căsuță gri - litera nu aparține cuvântului
  • căsuță galbenă - litera aparține cuvântului, dar se află pe altă poziție
  • căsuță verde - litera aparține cuvântului și se află pe poziția corectă

Descriere generală

Utilizatorul interacționează cu jocul prin intermediul tastaturilor matriceale 4×4, fiecare literă fiind reprezentată printr-un buton al tastaturilor. De asemenea, există pe a doua tastatură un buton ce are asociată acțiunea de Enter (înregistrarea cuvântului în joc) și un buton cu acțiunea de ștergere a unei litere din cuvânt. Pe ecranul LCD există un buton de reset, a cărui apăsare declanșează reînceperea jocului. Întregul joc este afișat pe ecranul LCD, iar în momentul finalizării jocului se aprinde unul dintre cele două led-uri, în funcție de rezultatul jucătorului: dacă cuvântul a fost ghicit, se aprinde led-ul verde, iar dacă jucătorul nu a ghicit cuvântul, se aprinde cel roșu.

Hardware Design

Listă de piese utilizate:

  • Arduino MEGA 2560
  • două tastaturi matriceale 4×4
  • ecran LCD 3.5’’
  • led-uri și rezistori
  • baterie 9V

Schema electrică, realizată în Fusion:

Close-up label-uri:

Componentele sunt conectate la placă astfel:

  • cele două tastaturi matriceale sunt conectate la 8 pini fiecare, prima la pinii analogici A4 - A11, iar a doua la pinii digitali D4 - D11
  • ecranul LCD este conectat conform schemei electrice la pinii digitali de jos, fiind conectat și la pin-ul RESET al plăcii
  • cele două led-uri sunt conectate la pinii digitali D2 și D3, ambele led-uri fiind conectate la rezistori pentru limitarea curentului

Software Design

Am realizat implementarea software în mediul de dezvoltare PlatformIO. Limbajul utilizat pentru dezvoltarea proiectului este C++.

Biblioteci utilizate:

  • TFT_HX8357 - afișarea jocului pe ecranul LCD
  • Keypad - configurarea interacțiunii dintre tastaturi și placă

Selectarea cuvântului

La începutul fiecărui joc, programul alege în mod aleator un cuvânt pe care utilizatorul trebuie să îl ghicească. Pentru stocarea cuvintelor de ghicit, am creat o matrice de caractere, pe fiecare linie a acesteia aflându-se un cuvânt. Pentru alegerea aleatoare a unui cuvânt, am utilizat funcția analogRead() pe un pin analogic care nu era conectat la nimic, aceasta întorcând un număr random.

// selectarea unui cuvânt random
  random_row = analogRead(A15) % 50;
  for(int i = 0; i < 6; i ++) {
      selected_word[i] = all_words[random_row][i];
  }

Implementarea desfășurării jocului

În funcția loop() am implementat desfășurarea unui joc complet. Utilizatorul are 6 încercări pentru a ghici cuvântul, interacționând în fiecare rundă cu tastaturile astfel:

  • poate apăsa pe tastele de la A la Z pentru a completa cuvântul din runda respectivă - poziționarea fiecărei litere pe ecran este determinată de coordonatele letter_x și letter_y, calculate în funcție de rundă și de numărul literei în cuvânt
  • poate apăsa pe tasta DELETE pentru a șterge o literă introdusă anterior, cu mențiunea că poate șterge doar litere ce aparțin de cuvântul curent - pentru ștergerea unei litere, desenez pe ecran un pătrat peste cel curent, mascând litera care trebuie ștearsă
  • poate apăsa pe tasta ENTER pentru a înregistra cuvântul din runda respectivă, tasta funcționând doar dacă au fost introduse exact 5 litere - după apăsarea tastei ENTER, programul verifică literele comune ale cuvântului introdus cu cele ale cuvântului de ghicit, redându-se o animație în care pătratele sunt colorate în funcție de corespondențele literelor
  • menționez că există 4 taste pe cea de-a doua tastatură care nu au nicio funcționalitate, apăsarea lor neproducând schimbări în joc
  • pentru a începe un joc nou, utilizatorul trebuie să apese pe butonul de pe LCD

Voi introduce bucăți din codul funcției loop(), pentru o înțelegere mai aprofundată a implementării, unde:

  • pressedLetter1 - tasta apăsată
  • letter_x, letter_y, box_x, box_y - coordonate pentru literă și pătrat
  • guess - vector în care stochez cuvântul din runda curentă
  • i - poziția literei în cuvânt

Afișarea unei litere pe ecran

if(pressedLetter1 && pressedLetter1 != '#' && pressedLetter1 != '*' && pressedLetter1 != '1' && pressedLetter1 != '2'
              && pressedLetter1 != '3' && pressedLetter1 != '4' && i < 5) {
 
          // afișarea literei apăsate
          tft.setTextColor(TFT_WHITE);
          tft.setCursor(letter_x, letter_y);
          tft.setTextFont(1);
          tft.setTextSize(3);
          tft.print(pressedLetter1);
 
          // updatarea coordonatelor pentru următoarea literă
          letter_x += 60;
          box_x += 60;
 
          // updatarea cuvântului curent
          guess[i] = pressedLetter1;
          i ++;
          continue;
        }

Ștergerea unei litere

if(pressedLetter2 && pressedLetter2 == '#') {
 
          // verificăm să nu ieșim din parametrii zonei de joc
          if(letter_x >= 92 && letter_x <= 332 && i >= 1 && i <= 5) {
            // updatăm coordonatele pentru a ne întoarce la litera anterioară
            box_x -= 60;
            letter_x -= 60;
            i --;
            // ștergerea literei prin desenarea unui pătrat peste cel existent
            tft.drawRect(box_x, box_y, 50, 50, SQUARE_INITIAL);
            tft.fillRect(box_x, box_y, 50, 50, SQUARE_INITIAL);
            // updatare cuvânt ghicit
            guess[i] = '0';
          }
          continue;
        }

Apăsarea tastei ENTER

if(pressedLetter2 && pressedLetter2 == '*' && i == 5) {
          valid_enter = 1;
          box_x = 15;
          letter_x = 32;
          // verificăn ce litere din cuvântul introdus corespund cu cele din cuvântul care trebuie ghicit
          validateGuess(selected_word, guess, box_x, box_y, letter_x, letter_y);
 
          // verficăm dacă s-a ghicit cuvântul
          if(isGuessCorrect(selected_word, guess)) {
            end_game = true;
            delay(1000);
 
            // aprindem led-ul verde pentru câștig
            digitalWrite(ledPinGreen, HIGH);
            continue;
          }
          // verificam dacă s-a terminat jocul și cuvântul nu a fost ghicit 
          if(!isGuessCorrect(selected_word, guess) && tries == 0) {
            end_game = true;
            delay(1000);
 
            // aprindem led-ul roșu pentru pierdere
            digitalWrite(ledPinRed, HIGH);
            continue;
          }
        }

Verificarea cuvintelor

Pentru verificarea literelor comune dintre cuvântul introdus și cel care trebuie ghicit, am creat funcția checkWord(), care returnează un șir ce conține caracterele 0, 1 sau 2 corespunzătoare celor 5 litere, care este folosit de funcția validateGuess() pentru a crea animația astfel:

  • 0 - pătratul este colorat gri
  • 1 - pătratul este colorat galben
  • 2 - pătratul este colorat verde

Funcția de realizare a animației

void validateGuess(char selected_word[], char guess[], int box_x, int box_y, int letter_x, int letter_y) {
  String color_codes = checkWord(selected_word, guess);
  for(int i = 0; i < 5; i ++) {
      delay(500);
 
      colorBox(color_codes, box_x, box_y, i);
      writeLetter(guess, letter_x, letter_y, i);
 
      box_x += 60;
      letter_x += 60;
    }
}

Afișarea rezultatelor

În urma finalizării jocului, pe ecran apare un mesaj în funcție de rezultatul obținut:

  • “YOU WIN!:)” - câștig
  • “YOU LOSE!:( The word was cuvânt_de_ghicit” - pierdere

Am implementat această funcționalitate folosind întreruperi, care se declanșau în urma detectării trecerii pinilor led-urilor din starea LOW în starea HIGH. De asemenea, am configurat led-urile pe pinii 2 și 3, care suportă întreruperi pe ATMEGA.

// în funcția setup()
  attachInterrupt(digitalPinToInterrupt(ledPinGreen), ISRCastig, RISING);
  attachInterrupt(digitalPinToInterrupt(ledPinRed), ISRPierdere, RISING);
 
// funcția ISR pentru câștig
void ISRCastig() {
  tft.setTextColor(TFT_GREEN);
  tft.setCursor(50, 420);
  tft.setTextFont(1);
  tft.setTextSize(4);
  tft.print("YOU WIN!:)");
}
 
// funcția ISR pentru pierdere
void ISRPierdere() {
  tft.setTextColor(TFT_RED);
  tft.setCursor(70, 420);
  tft.setTextFont(1);
  tft.setTextSize(3);
 
  tft.print("YOU LOSE!:(");
 
  tft.setTextColor(TFT_WHITE);
  tft.setCursor(55, 450);
  tft.setTextFont(1);
  tft.setTextSize(2);
 
  tft.print("The word was ");
  tft.setTextColor(TFT_GREEN);
  tft.print(selected_word);
}

Rezultate Obţinute

În aceste poze se observă aspectul jocului în urma câștigării sau pierderii unui joc.

Am înregistrat, de asemenea, și un videoclip cu o demonstrație a jocului: Wordle - Proiect PM

Concluzii

Pentru mine, realizarea acestui proiect a fost foarte interesantă, atât datorită faptului că am avut ocazia de a implementa unul dintre jocurile mele preferate, dar și deoarece am experimentat pentru prima oară realizarea unui proiect ce implică o placă Arduino de la 0, realizând atât conectarea acesteia de celelalte componente hardware, cât și implementarea codului.

Download

marian_melania_proiect_pm.zip - Arhivă Proiect

Jurnal

  • 22.04 - Alegere temă proiect
  • 30.04 - Creare pagină OCW
  • 04.05 - Comandare componente hardware
  • 05.05 - Documentație inițială OCW
  • 16.05 - Schemă electrică + testare componente hardware
  • 19.05 - Finalizare hardware
  • 25.05 - Finalizare software
  • 26.05 - Finalizare pagină OCW

Bibliografie/Resurse

Resurse Hardware

Resurse Software

  1. TFT_HX8357 - bibliotecă LCD
  2. Keypad - bibliotecă tastaturi matriceale
  3. Laborator 0 - LED-uri
  4. Laborator 2 - Întreruperi
  5. Laborator 5 - SPI, folosit de ecranul LCD
pm/prj2024/fgul/melania.marian.txt · Last modified: 2024/05/26 20:06 by melania.marian
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0