English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Proseguimento dell'implementazione del Tetris in C++

Primo, introduzione all'esperimento

1.1 Contenuto dell'esperimento

In questa sezione dell'esperimento realizzeremo la progettazione delle funzioni principali del Tetris, completiamo le funzionalità di base e eseguiamo.

1.2 Conoscenze sperimentali

Disegno della finestra
Progettazione della classe quadrato
Algoritmo di rotazione
Funzione di spostamento e rimozione

1.3 Ambiente sperimentale

Terminale xface
Compilatore g++
Libreria ncurses

1.4 Compilare il programma

L'ordine di compilazione deve includere l'opzione -l per introdurre la libreria ncurses:

g++ main.c -l ncurses

1.5 Eseguire il programma

./a.out

1.6 Risultato dell'esecuzione


Secondo, passi sperimentali

2.1 File di intestazione

Prima includere i file di intestazione e definire una funzione di scambio e una funzione casuale, utilizzate in seguito (la funzione di scambio viene utilizzata per ruotare i quadrati, la funzione casuale viene utilizzata per impostare la forma dei quadrati)

#include <iostream>
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
/* Scambiare a e b */
void swap(int &a, int &b){
 int t=a;
 a = b;
 b = t;
}
/* Ottenere un numero intero casuale nell'intervallo (min,max) */
int getrand(int min, int max)}
{
 return(min+rand()%(max-min+1));
}

2.2 Definisci la classe

Poiché il contenuto del programma è relativamente semplice, qui viene definita solo una classe Piece

class Piece
 {
 public:
  int score;  //Punteggio
  int shape;  //Rappresenta la forma del blocco corrente
  int next_shape;  //Rappresenta la forma del prossimo blocco
  int head_x;  //Posizione del primo box del blocco corrente, etichetta di posizione
  int head_y;
  int size_h;  //Dimensioni del blocco corrente
  int size_w;
  int next_size_h;  //Dimensioni del prossimo blocco
  int next_size_w;
  int box_shape[4][4]; //Array 4x4 per la forma del blocco corrente
  int next_box_shape[4][4];  //Array 4x4 per la forma del prossimo blocco
  int box_map[30][45];  //Usato per etichettare ogni box all'interno della scatola di gioco
  bool game_over;  //Segnale di fine gioco
 public:
  void initial();  //Funzione di inizializzazione
  void set_shape(int &cshape, int box_shape[][4],int &size_w, int & size_h);  //Imposta la forma del blocco
  void score_next();  //Mostra la forma e il punteggio del prossimo blocco
  void judge();  //Determina se il livello è pieno
  void move(); //Funzione di movimento controllata con ← → ↓
  void rotate(); //Funzione di rotazione
  bool isaggin(); //Determina se il prossimo movimento andrà oltre i limiti o si sovrapporrà
  bool exsqr(int row); //Determina se la riga corrente è vuota
 };

2.3 Imposta la forma del blocco

Qui viene definita tramite la语句case 7 forme diverse di blocchi, che devono essere chiamate prima di ogni nuova caduta di blocco per impostare correttamente la forma e la posizione iniziale

void Piece::set_shape(int &cshape, int shape[][4],int &size_w,int &size_h)
{
 /*Inizializza l'array 4x4 utilizzato per rappresentare con 0*/
 int i,j;
 for(i=0;i<4;i++)
  for(j=0;j<4;j++)
   shape[i][j]=0;
 /*Imposta 7 forme iniziali e imposta le loro dimensioni*/
 switch(cshape)
 {
  case 0: 
   size_h=1;
   size_w=4; 
   shape[0][0]=1;
   shape[0][1]=1;
   shape[0][2]=1;
   shape[0][3]=1;
   break;
  case 1:
   size_h=2;
   size_w=3;
   shape[0][0]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 2:
   size_h=2;
   size_w=3; 
   shape[0][2]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 3:
   size_h=2;
   size_w=3;
   shape[0][1]=1;
   shape[0][2]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   break;
  case 4:
   size_h=2;
   size_w=3;
   shape[0][0]=1;
   shape[0][1]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
  case 5: 
   size_h=2;
   size_w=2;
   shape[0][0]=1;
   shape[0][1]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   break;
  case 6: 
   size_h=2;
   size_w=3;
   shape[0][1]=1;
   shape[1][0]=1;
   shape[1][1]=1;
   shape[1][2]=1;
   break;
 }
 // Dopo aver impostato la forma, inizializzare la posizione iniziale del pezzo
 head_x=game_win_width/2;
 head_y=1;
 // Se si inizializza e si sovrappone immediatamente, la partita è finita~
 if(isaggin()) /* GAME OVER ! */
  game_over=true;
}

2.4 Funzione di rotazione

Ecco un algoritmo semplice per ruotare il pezzo, simile alla rotazione di una matrice, prima si verifica la simmetria diagonale della matrice shape, poi la simmetria orizzontale, completando così la rotazione. È necessario verificare se il pezzo esce dai limiti o si sovrappone dopo la rotazione. Se lo fa, annulla la rotazione. Attenzione!

void Piece::rotate()
 {
  int temp[4][4]={0}; // Variabile temporanea
  int temp_piece[4][4]={0}; // Array di backup
  int i,j,tmp_size_h,tmp_size_w;
  tmp_size_w=size_w;
  tmp_size_h=size_h;
  for(int i=0; i<4;i++)
   for(int j=0;j<4;j++)
    temp_piece[i][j]=box_shape[i][j]; // Salva la forma attuale del pezzo, se la rotazione fallisce tornare alla forma attuale
  for(i=0;i<4;i++)
   for(j=0;j<4;j++)
    temp[j][i]=box_shape[i][j]; // Simmetria diagonale
  i=size_h;
  size_h=size_w;
  size_w=i;
  for(i=0;i<size_h;i++)
   for(j=0;j<size_w;j++)
    box_shape[i][size_w-1-j]=temp[i][j]; // Simmetria orizzontale
  /* Se la rotazione successiva causasse una sovrapposizione, tornare alla forma di backup dell'array */
  if(isaggin())
   for(int i=0; i<4;i++)
    for(int j=0;j<4;j++)
     box_shape[i][j]=temp_piece[i][j];
   size_w=tmp_size_w; // Ricorda di tornare la size all'originale
   size_h=tmp_size_h;
  }
  /* Se la rotazione è riuscita, mostrala sullo schermo */
  else{
   for(int i=0; i<4;i++)
    for(int j=0;j<4;j++){
     if(temp_piece[i][j]==1){
      mvwaddch(game_win,head_y+i,head_x+j,' '); // Muoversi al punto di coordinate specifiche nel finestra game_win e stampare il carattere
      wrefresh(game_win);
     }
    }
   for(int i=0; i<size_h;i++)
    for(int j=0;j<size_w;j++){
     if(this->box_shape[i][j]==1){
      mvwaddch(game_win,head_y+i,head_x+j,'#');
      wrefresh(game_win);
     }
   }
  }
}

2.5 Funzione di movimento

Se il giocatore non preme alcun tasto, la scatola deve cadere lentamente, quindi non possiamo bloccarci in getch() aspettando l'input del tasto, qui viene utilizzato select() per annullare il blocco.

/* Qui è stato estratto solo un parte del programma, per l'implementazione dettagliata si prega di consultare il codice sorgente */
struct timeval timeout;
 timeout.tv_sec = 0;
 timeout.tv_usec= 500000;
if (select(1, &set, NULL, NULL, &timeout) == 0)

timeout è il tempo massimo di attesa per il tasto, qui è impostato a 500000us, se supera questo tempo non si aspetta più l'input di getch(), si procede direttamente al passo successivo.

Se viene rilevato un tasto entro il tempo di timeout, la seguente istruzione if è vera, si ottiene il valore di input key, attraverso la valutazione di diversi valori di key si eseguono operazioni come muoversi a sinistra, destra, in basso, ruotare ecc.

if (FD_ISSET(0, &set))
    while ((key = getch()) == -1) ;
Il modo di gestione delle funzioni per muoversi a sinistra, destra e in basso è基本上相同,qui solo si spiega la funzione per muoversi in basso

/* Qui è stato estratto solo un parte del programma, per l'implementazione dettagliata si prega di consultare il codice sorgente */
/* Se il tasto premuto è ↓ */
if(key==KEY_DOWN){
  head_y++; // Incrementa la coordinata y del blocco
  if(isaggin()){ // Se si sovrappone o esce dai limiti, annulla questa movimentazione
   head_y--;
   /* Se è stato fermato, allora impostare la box corrispondente sulla mappa come occupata, utilizzando 1 per rappresentare e 0 per rappresentare che non è occupata */
   for(int i=0;i<size_h;i++)
    for(int j=0;j<size_w;j++)
     if(box_shape[i][j]==1)
      box_map[head_y+i][head_x+j]=1;
   score_next(); //Display the score and hint the next block
  }
  /*If it can move down, then cancel the display of the current block, move down one row for display, and pay attention to the line of the for loop from bottom to top
  else{
   for(int i=size_h-1; i>=0;i--)
    for(int j=0;j<size_w;j++){
     if(this->box_shape[i][j]==1){
      mvwaddch(game_win,head_y-1+i,head_x+j,' ');
      mvwaddch(game_win,head_y+i,head_x+j,'#');
     }
    }
   wrefresh(game_win);
}

2.6 Repeated function

The function to be judged after each move or rotation. If the function returns true, it cannot move, and if it returns false, the next step can be taken.

bool Piece::isaggin(){
 for(int i=0;i<size_h;i++)
  for(int j=0;j<size_w;j++){
   if(box_shape[i][j]==1){
    if(head_y+i > game_win_height-2) //Out of bounds below
     return true;
    if(head_x+j > game_win_width-2 || head_x+i-1<0) //Out of bounds on the left and right
     return true;
    if(box_map[head_y+i][head_x+j]==1) //Overlaps with an occupied box
     return true;
   }
  }
 return false;
}

2.7 Function of layer full

The last very important function is to clear the rows that are full of blocks. This needs to be judged every time a block stops moving downward.

void Piece::judge(){
 int i,j;
 int line=0; //Used to record the number of full layers
 bool full;
 for(i=1;i<game_win_height-1;i++){ //Exclude boundaries
  full=true;
  for(j=1;j<game_win_width-1;j++){
   if(box_map[i][j]==0) //There is an unoccupied box
    full=false; //It means that this layer is not full
  }
  if(full){ //If the layer is full
   line++; //Increase the number of full rows by 1
   score+=50; //Add points~
   for(j=1;j<game_win_width-1;j++)
    box_map[i][j]=0; //Clear this layer (mark as unoccupied)
  }
 }
 /*After the above judgment, check the value of line. If it is not 0, it means that a layer is full and needs to be cleared*/
 if(line!=0){
 for(i=game_win_height-2;i>=2;i--){
  int s=i;
  if(exsqr(i)==0){
   while(s>1 && exsqr(--s)==0); // Trova la riga esistente del blocco e muovila verso il basso
   for(j=1;j<game_win_width-1;j++){
    box_map[i][j]=box_map[s][j]; // Spostamento del livello superiore
    box_map[s][j]=0; // Pulizia del livello superiore
   }
  }
 }
 /*Dopo aver pulito e spostato i segni, è necessario aggiornare lo schermo, ri stampare game_win*/
 for(int i=1;i<game_win_height-1;i++)
   for(int j=1;j<game_win_width-1;j++){
    if(box_map[i][j]==1){
     mvwaddch(game_win,i,j,'#');
     wrefresh(game_win);
    }
    else{
     mvwaddch(game_win,i,j,' ');
     wrefresh(game_win);
    }
   }
 }
}

Terza sezione: Sommario sperimentale

Con questo, la presentazione di alcune funzioni chiave è completa. Comprendere le funzioni di queste funzioni e realizzarle, quindi consultare il codice sorgente per completare altre funzioni e la funzione main può essere eseguita! Naturalmente, ci sono molti modi per implementare il tetris russo, e il pensiero e i metodi di ciascuno possono essere diversi. Forse il tuo tetris russo sarà più semplice e più fluido! Enjoy it!:)

Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, è di proprietà del rispettivo autore, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, il sito web non detiene il diritto di proprietà, non è stato editato manualmente e non assume responsabilità per le relative responsabilità legali. Se trovi contenuti sospetti di copyright, ti preghiamo di inviare una e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare, fornendo prove pertinenti. Una volta verificata, il sito web rimuoverà immediatamente i contenuti sospetti di copyright.

Ti potrebbe interessare