首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >简单俄罗斯方块游戏

简单俄罗斯方块游戏
EN

Code Review用户
提问于 2021-02-03 17:43:32
回答 3查看 1.3K关注 0票数 5

我做了这个简单的俄罗斯方块游戏,我只有两个街区,正方形和线。此外,游戏结束后,20个街区被创造和球员获胜。我在想怎样才能让它更好。

下面是代码:

代码语言:javascript
复制
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <time.h>

#define RIGA 21
#define COLONNA 10
#define DESTRA 77
#define SINISTRA 75
#define GIU 80
#define SU 72

void GotoXY(int x, int y)
{
    COORD CursorPos = {x, y};
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hConsole, CursorPos);
}

// FUNCTION PER RALLENTARE I MOVIMENTI
void ritardo()
{
    long double i;
    for(i=0;i<=(30000000);i++);
}

int i, j; // CONTATORI
int sposta, random; // VARIABILI BOOLEANE
int punti = 0; // VARIABILE PER ASSEGNAZIONE DEI PUNTI
int contatore_pezzi = 1; // VARIABILE PER CONTARE IL NUMERO DI OGGETTI CREATI
char griglia[21][10]; // DICHIARAZIONE ARRAY DI CHAR
char pezzo[2][2] = {{'X', 'X'}, // DICHIARAZIONE CUBO
                    {'X', 'X'}};
char linea[3][1] = {{'X'},
                    {'X'},
                    {'X'}}; // DICHIARAZIONE LINEA
char linea_or[1][3] = { {'X'},{'X'},{'X'}};

void caricaArray() // INIZIALIZZA L'ARRAY VUOTO CON CICLI FOR INNESTATI
{
    for (i = 0; i < RIGA; i++)
    {
        for (j = 0; j < COLONNA; j++)
        {
            griglia[i][j] = ' ';
        }
    }
}

// PROTOTIPI FUCTION
void movimenti();
void assegnaPezzo();
void eliminaRiga();
void stampaBordi();

int main()
{
    caricaArray();
    movimenti();
    printf("\n\n");
    return 0;
}

void visualizzaArray() // VISUALIZZA L'ARRAY ALLO STATO ATTUALE
{
    printf("\n");
    for (i = 0; i < RIGA; i++)
    {
        printf(" "); //Spazio per visualizzare tutti i punti all'interno del bordo
        for (j = 0; j < COLONNA; j++)
        {
            printf("%c ", griglia[i][j]);
        }
        printf("\n");
    }
        stampaBordi();
}

void movimenti() // GESTISCE TUTTI I POSSIBILI MOVIMENTI DELLA FORMA
{
    do {
        eliminaRiga(); // Ad ogni ciclo verifica la presenza di righe piene
        int r = 0;
        int c = 4;
        srand(time(NULL)); // Random
        random = rand() % 2;
        assegnaPezzo();
        visualizzaArray();

        do {
            GotoXY(r,c); // Inizia a scorrere verso il basso
            r++;
            system("cls");
            if (random == 0) { // Se l'oggetto creato e' il cubo
                griglia[r-1][c] = ' '; // Libera le posizioni precedenti
                griglia[r-1][c+1] = ' ';
                griglia[r][c] = pezzo[0][0]; // Riassegna l'oggetto nella nuova posizione
                griglia[r][c+1] = pezzo[0][1];
                griglia[r+1][c] = pezzo[1][0];
                griglia[r+1][c+1] = pezzo[1][1];
            }
            if (random == 1) { // Se l'oggetto creato e' la linea
                    if ( !kbhit() ) // Se non si premono tasti
                        {
                            griglia[r-1][c] = ' '; // Libera le posizioni precedenti
                            griglia[r][c] = linea[0][0]; // Riassegna l'oggetto nella nuova posizione
                            griglia[r+1][c] = linea [1][0];
                            griglia[r+2][c] = linea [2][0];
                        }
            }
            visualizzaArray();
            ritardo();
            if ( kbhit() ) // Se durante lo scroll verticale si preme un tasto
                {
                    sposta = getch(); // Prende in input un tasto e lo assegna alla variabile sposta
                    if (sposta==SU) // E se e' la freccia SU
                        {
                            griglia[r-1][c] = ' ';
                            griglia[r][c-1] = linea_or[0][0];
                            griglia[r][c] = linea_or[1][0];
                            griglia[r][c+1] = linea_or[2][0];
                        }

                    if (sposta == DESTRA ) // E se e' la freccia DESTRA
                    {
                        c++; // L'oggetto si sposta a destra
                        system("cls");
                        if (random == 0) {
                            if (c+1>COLONNA-1) { break; } // Se supera il bordo destro
                        griglia[r][c-1] = ' '; // Libera le posizioni precedenti
                        griglia[r+1][c-1] = ' ';
                        }
                        else if (random == 1) {
                        griglia[r-2][c-1] = ' '; // Libera le posizioni precedenti
                        griglia[r-1][c-1] = ' ';
                        griglia[r][c-1] = ' ';
                        griglia[r+1][c-1] = ' ';
                        griglia[r+2][c-1] = ' ';
                        }
                        visualizzaArray(); //visualizza il tutto
                    }
                    if (sposta == SINISTRA ) // E se e' la freccia SINISTRA
                    {
                        c--; // L'oggetto si sposta a sinistra
                        if (c<0) { break; } // Se supera il bordo sinistro
                        system("cls");
                        if (random == 0) {
                        griglia[r][c+2] = ' '; // Libera le posizioni precedenti
                        griglia[r+1][c+2] = ' ';
                        }
                        else if (random == 1) {
                        griglia[r-1][c+2] = ' '; // Libera le posizioni precedenti
                        griglia[r-1][c+1] = ' ';
                        griglia[r][c+1] = ' ';
                        griglia[r+1][c+1] = ' ';
                        griglia[r+2][c+1] = ' ';
                        }
                        visualizzaArray();
                    }
                }
            if (random == 0) {
            if (griglia[r+2][c] == 'X' || griglia[r+2][c+1] == 'X') // Se il pezzo tocca un altro pezzo inferiormente
            {
                if (griglia[0][c] == 'X' || griglia[1][c] == 'X') // Se lo spazio superiore e' troppo piccolo
                {
                    system("cls");
                    printf("HAI PERSO!\nPremi ESC per uscire.");
                    break;
                }
                contatore_pezzi++;
                return movimenti(); //Allora attiva la ricorsivita'
            }
            }
            if (random == 1) {
                if (griglia[r+3][c] == 'X') {
                    if (griglia[0][c] == 'X' || griglia[1][c] == 'X')
                    {
                        system("cls");
                        printf("HAI PERSO!\nPremi ESC per uscire.");
                        break;
                    }
                    contatore_pezzi++;
                    return movimenti();
                }
            }
            if (random == 0) {
            if (r+1==20) { // Se tocca il borso inferiore crea un altro oggetto
                contatore_pezzi++;
                return movimenti(); }
            }
            else if (random == 1) {
            if (r+2==20) { // Se tocca il borso inferiore crea un altro oggetto
                contatore_pezzi++;
                return movimenti(); }
            }
        } while ( sposta != 27 );
    } while ( getch() != 27 );
}

void stampaBordi() // STAMPA LA CORNICE DEL CAMPO DI GIOCO
{
    for(i=1;i<=19;i++)
    {
        GotoXY(i,22); putch('-');
        GotoXY(i,0); putch('-');
    }
        printf("   PUNTI:%d - PEZZO No %d", punti, contatore_pezzi);
    for (j=1;j<22; j++) {
        GotoXY(0,j); putch('|');
        GotoXY(20,j); putch('|');
    }
}

void assegnaPezzo()
{
    int i1 = 0;
    int j1 = 4;
    if (random==0) {
    griglia[i1][j1] = pezzo[0][0]; // Assegna il pezzo nella posizione iniziale
    griglia[i1][j1+1] = pezzo[0][1];
    griglia[i1+1][j1] = pezzo[1][0];
    griglia[i1+1][j1+1] = pezzo[1][1];
    }
    if (random==1) {
        griglia[i1][j1] = linea[0][0];
        griglia[i1+1][j1] = linea [1][0];
        griglia[i1+2][j1] = linea [2][0];
    }
}

void eliminaRiga() // ELIMINA UNA O PIU' RIGHE, SE PIENE
{
    int k;
    int cont_riga;
    int colonna;
    char aus[COLONNA];
    for (cont_riga=0; cont_riga<=20; cont_riga++)
    {
        if (griglia[cont_riga][0] == 'X' && griglia[cont_riga][1] == 'X' && griglia[cont_riga][2] == 'X' && griglia[cont_riga][3] == 'X' && griglia[cont_riga][4] == 'X' && griglia[cont_riga][5] == 'X' && griglia[cont_riga][6] == 'X' && griglia[cont_riga][7] == 'X' && griglia[20][8] == 'X' && griglia[20][9] == 'X')
        {
            for (colonna=0; colonna<10; colonna++) {
                aus[colonna] = griglia[cont_riga-1][colonna];
            }
            for (colonna=0; colonna<10; colonna++) {
                griglia[cont_riga][colonna] = aus[colonna];
            }
            for (colonna=0; colonna<10; colonna++) {
                griglia[cont_riga-1][colonna] = '.';
            }
            punti++;
            for (k=2; k<11; k++) {
            if (griglia[cont_riga-k][0] == 'X' || griglia[cont_riga-k][1] == 'X' || griglia[cont_riga-k][2] == 'X' || griglia[cont_riga-k][3] == 'X' || griglia[cont_riga-k][4] == 'X' || griglia[cont_riga-k][5] == 'X' || griglia[cont_riga-k][6] == 'X' || griglia[cont_riga-k][7] == 'X' || griglia[cont_riga-k][8] == 'X' || griglia[cont_riga-k][9] == 'X' )
                {
                    for (colonna=0; colonna<10; colonna++) {
                    aus[colonna] = griglia[cont_riga-k][colonna];
                    }
                    for (colonna=0; colonna<10; colonna++) {
                    griglia[cont_riga-k+1][colonna] = aus[colonna];
                    }
                    for (colonna=0; colonna<10; colonna++) {
                    griglia[cont_riga-k][colonna] = '.';
                    }
                }
            }
            system("cls");
            visualizzaArray();
        }
    }
}
EN

回答 3

Code Review用户

发布于 2021-02-04 08:32:10

  • 始终用英语编写源代码和注释(我也不是英语母语)。您迟早需要与世界各地的其他人共享代码,就像您刚才在这里和现在所做的那样。因此,它需要用英语。更重要的是,当你只需要学习一个的时候,学习两个技术编程术语是疯狂的。我在大学学习工程学时犯的最严重的错误是阅读翻译成我自己语言的书,而不是英语。这意味着,我必须学习两倍的技术术语:真正的,正确的英语术语,和可想而知的翻译成我自己的语言。后者是完全无用的知识-在现实世界以外的学校,英语术语被使用。
  • 正如在另一篇评论中指出的,在这种情况下,ritardo繁忙的延迟应该被Windows Sleep所取代。在使用线程和事件的更高级的Windows编程中,您也不会使用Sleep,而是使用WaitForSingleObject等。多线程是一个比较高级的主题,但是您最终需要学习这个主题。然后这个程序将使用一个线程来绘制图形,一个线程用于输入,另一个线程用于计算。而不是使用旧的MS风格的kbhit - DOS没有线程。
  • 如果要实现一个繁忙的延迟循环,那么循环迭代器i需要声明为volatile。否则,编译器就会优化整个循环。
  • 不要对循环迭代器使用浮点数。它会使代码变慢,比较表达式可能会因浮点不准确而意外失败。
  • 始终尝试在for循环:for(int i=0; ...中声明循环迭代器。
  • 你需要去掉所有的全局变量。大量的信息,为什么他们是坏的被发现在网上。
  • 不要在C:void caricaArray()中使用过时的样式空括号函数。这意味着函数接受任何参数,这种样式在任何时候都可能从C中删除,因为它正式过时了。正确的形式是void caricaArray(void)。(不要与C++混淆,这里的空括号很好,相当于(void)。)
  • 正如在另一篇评论中所指出的,您需要确定编码风格并坚持它。支撑放置,缩进等,就其现状而言,代码几乎是可读的。
  • srand();只应该在程序开始时调用一次,在循环中调用它。
票数 6
EN

Code Review用户

发布于 2021-02-03 22:43:15

有些建议没有具体的顺序:

  1. 组织您的头文件。我喜欢把相关的事情组合在一起。在这种情况下,“标准”与“特定于操作系统”。如果没有其他理由,我也默认按字母/按顺序排序。
  2. 分组相关的事情:函数与函数,变量与变量。不要将函数和全局变量随机混合。
  3. 除非必须,否则不要将变量设置为全局变量。ij应该是局部变量。
  4. 俄罗斯方块线有4个街区,而不是3个。
  5. 在可能的情况下使用库函数。您的ritardo()似乎是POSIX sleep()或WindowsSleep()的糟糕替代品。
  6. 使用memsetcaricaArray中初始化您的griglia:memset(griglia,SPACE,sizeof(griglia));
  7. 标准C要求使用void对不带参数的函数进行原型。(C++允许空父母表示没有参数。)因此,像: void ();这样的一行实际上并不是一个原型,而是一个声明。
  8. 打开所有可能的警告。您的编译器在某个地方设置了一些将启用“所有警告”的设置。然后,它可能有一些设置,可以启用“不寻常的警告”或“额外的警告”或其他什么。它甚至可能设置为“启用令人难以置信的学究,荒谬的警告”。把他们打开。他们所有人。
  9. main()的定义应该是第一个函数,或者是最后一个函数。如果您喜欢“读取我的程序”,那么main是第一位的,并且函数将随着它们的使用而附加到末尾(主调用A、A调用B等)。如果您倾向于“避免前向声明”,那么在调用每个函数之前都会尝试定义它。所以最低级别的函数位于文件的顶部,然后是更高的,最后是main()。如果你喜欢“一切都应该按字母顺序排列”,那么你就是个精神病患者,但至少你知道事情该在哪里。这是一个风格的问题,所以选择一个并坚持下去。
  10. 不要混淆终端模式。如果要定位光标和绘制字符,那么编写自己的打印函数(管理滚动窗口,如果没有其他的话)。另外,不要调用cls来清除屏幕。写你自己的函数。
  11. 编写更多的函数--您有一些非常大的函数。关于如何使函数具有良好的大小,有很多指导方针,但大多数准则都集中在使函数在单个屏幕上可见的方面。(有些人认为数字很低。)如果你试着用一个很小的数字,你会惊讶于你能做什么!)作为一种建议,如果你在不同的地方做同样的事情,那应该是一种功能。如果你用不同的方式做同样的事情--例如,画正方形或画一条线--那么你可能需要坐下来思考如何以同样的方式对待它们。也许您需要一个数据结构来告诉您大小是多少?或者您可以使用一个指针和一个整数来告诉您所指向的是什么?enum型?
  12. 缓存kbhit()的结果。该函数检查键是否已被击中。这意味着每次你打电话的时候,你都要做另一次检查。在循环中,可能更容易在循环顶部调用它一次,记住结果,然后等到下一次遍历循环时再调用它。
  13. 选择一个程序设计风格,并一直使用它,直到程序结束!我在您的程序中看到了三个压痕样式 (好吧!):if (sposta==SU) // the e‘la freccia SU { grigliar-1 =’';grigliaR = linea_or0;grigliaR = linea_or1;grigliaR = linea_or2;}这是"GNU“。除非你个人欠理查德·斯泰尔曼钱,否则不要用它。如果(sposta == DESTRA ) // e‘la freccia DESTRA {这是"Allman风格“。c++;// L‘’oggetto si sposta a destra系统(“cls”);if (随机== 0) {这是"One True Brace样式“或"K &R样式”,取决于您如何处理函数。如果(c+1>COLONNA-1) { break;} // Se supera il il bordo destro,这是“强制Braces变体”,即a.k.a。“旗面”风格。所以,只要选择一个GNU,Allman,K&R,或BannerPlane。我的错,四种风格。
  14. 到处使用名为#defines或enum常量的常量。您已经有了一个良好的开端,但是随后您会陷入诸如'X'20这样的值的泥潭。20是什么?

也许还有更多的话要说,但现在看来已经足够了。

票数 5
EN

Code Review用户

发布于 2021-02-04 12:59:16

总的来说,它看起来是相当不错的过程代码。

范围

代码语言:javascript
复制
int i, j; // CONTATORI

这些索引可以是函数的局部索引。全球是好的,但在这种情况下不是。

而且,sposta可能总是movimenti()函数的本地函数。

幻数

代码语言:javascript
复制
void stampaBordi() // STAMPA LA CORNICE DEL CAMPO DI GIOCO
{
    for(i=1;i<=19;i++)
    {
        GotoXY(i,22); putch('-');
        GotoXY(i,0); putch('-');
  • 我不知道这些数字意味着什么(19,22),你可能几天/几个月/几年后
  • i是columnIndex还是numberOfIterations,或者其他类似的东西?

使用结构

代码语言:javascript
复制
char griglia[21][10]; // DICHIARAZIONE ARRAY DI CHAR

...
void stampaBordi() // STAMPA LA CORNICE DEL CAMPO DI GIOCO
{
    for(i=1;i<=19;i++)
    {
        GotoXY(i,22); putch('-');
        GotoXY(i,0); putch('-');

这些都与董事会有关。将这些变量组合在一起通常是个好主意,因为当相关的值是toghether (理解/可读性原因)时,更容易思考。

代码语言:javascript
复制
struct Grid  {
    char cells[21][10];
    int rows;
    int columns;
};

Grid grid = {
   rows = 21;
   columns = 10;
}
...
void stampaBordi(Grid * grid) // STAMPA LA CORNICE DEL CAMPO DI GIOCO
{
    for(int i=1;i<= grid->rows - 2;i++)
    {
        GotoXY(i, grid->rows + 1 ); putch('-');
        GotoXY(i, 0 ); putch('-');

我确信这些常量中的一些也应该是网格结构的成员:)

代码语言:javascript
复制
#define RIGA 21
#define COLONNA 10
#define DESTRA 77
#define SINISTRA 75
#define GIU 80
#define SU 72

压缩代码

在这种情况下..。

代码语言:javascript
复制
if (sposta == DESTRA ) // E se e' la freccia DESTRA
                    {
                        c++; // L'oggetto si sposta a destra
                        system("cls");
                        if (random == 0) {
                            if (c+1>COLONNA-1) { break; } // Se supera il bordo destro
                        griglia[r][c-1] = ' '; // Libera le posizioni precedenti
                        griglia[r+1][c-1] = ' ';
                        }
                        else if (random == 1) {
                        griglia[r-2][c-1] = ' '; // Libera le posizioni precedenti
                        griglia[r-1][c-1] = ' ';
                        griglia[r][c-1] = ' ';
                        griglia[r+1][c-1] = ' ';
                        griglia[r+2][c-1] = ' ';
                        }
                        visualizzaArray(); //visualizza il tutto
                    }

将相关信息提取到结构中,以便更容易地制作新的内容并阅读。

代码语言:javascript
复制
// code like this repeats on multiple places
// and it seems like You are working with an array of offsets
// and You are doing single operation to them

                        griglia[r-2][c-1] = ' '; // Libera le posizioni precedenti
                        griglia[r-1][c-1] = ' ';
                        griglia[r][c-1] = ' ';
                        griglia[r+1][c-1] = ' ';
                        griglia[r+2][c-1] = ' ';

// this can be compressed like this

struct Offset { int x; int y; }

Offset offsets = [{-2,-1},{-1,-1},{0,-1},{1,-1},{2,-1}];
int offsetCount = 5;

for(auto i = 0; i < offsetCount; i++) {
    auto offset = offsets[i];
    griglia[r + offset.x][c + offset.y] = ' ';
}

// and it can be pulled into function

clearOffsets( Offsets * offsets, int offsetCount )

// Also these offsets seem to be always asociated with a tetromino
// each tetromino has it's own offsets, so

struct Offset { int x; int y; }

struct Tetromino {  Offset cellOffsets[]; int offsetCount; ... }

Tetromino l_shape = { cellOffsets = [{-2,-1},{-1,-1},{0,-1},{1,-1},{2,-1}], 5}


// now You can make functions like this

clearTetromino(l_shape);
rotateTetrominoLeft(l_shape);

// also You can reuse function to rotate another shape or shapes ...

rotateTetrominoLeft(box_shape); 
rotateTetrominoRight(shapes[shapeIndex]);

有关代码压缩的更多信息,请参见优秀的https://caseymuratori.com/blog_0015

格式

代码语言:javascript
复制
            else if (random == 1) {
            if (r+2==20) { // Se tocca il borso inferiore crea un altro oggetto

这对我来说很难快速扫描,因为我已经习惯了K&R支撑风格和Allman支撑风格(大多数人都使用它们)。

注释

英文评论很好:)

我有这样的启发:我避免解释变量或函数的含义的注释,而是为函数/变量设置更好(更长、更描述性)的名称。我评论一些不太明显的东西.

这个没问题

代码语言:javascript
复制
            if (r+2==20) { // Se tocca il borso inferiore crea un altro oggetto

但是,也许通过引入变量来删除注释可以使事情变得清晰(而且您可以在多个地方重复使用变量)。

代码语言:javascript
复制
auto touchesLongerEdge = r + 2 === 20;
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/255574

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档