Code rắn săn mồi c++

Game này được phát hành vào những năm 70 trên các hệ máy cầm tay, một thời gian sau được đưa lên PC. Trong game người chơi sẽ điều khiển một con rắn. Mục tiêu của người chơi là cố gắng cho con rắn ăn được càng nhiều mồi càng tốt. Mỗi lần con rắn ăn mồi, cơ thể nó sẽ dài ra. Game kết thúc khi người chơi cho rắn “ăn” phải tường hoặc cơ thể chính mình.

Mô tả game

Kích thước của mỗi đốt trên con rắn là 10px. Con rắn sẽ được điều khiển bằng các phím mũi tên. Khi game kết thúc, dòng chữ “Game Over” sẽ hiện lên giữa màn hình.

#pragma once

#include 
#include 

class Snake : public QWidget {
    
  public:
      Snake(QWidget *parent = 0);

  protected:
      void paintEvent(QPaintEvent *);
      void timerEvent(QTimerEvent *);
      void keyPressEvent(QKeyEvent *);

  private:    
      QImage apple;
    
      static const int B_WIDTH = 300;
      static const int B_HEIGHT = 300;
      static const int DOT_SIZE = 10;
      static const int ALL_DOTS = 900;
      static const int RAND_POS = 29;
      static const int DELAY = 140;    
      
      int timerId;
      int dots;
      int apple_x;
      int apple_y;      
      
      int x[ALL_DOTS]; 
      int y[ALL_DOTS]; 
      
      int startAngle = 0;
      int spanAngle = 16 * 360;
      bool leftDirection;
      bool rightDirection;
      bool upDirection;
      bool downDirection;
      bool inGame;
      
      void loadImages();
      void initGame();
      void locateApple();
      void checkApple();
      void checkCollision();
      void move();
      void doDrawing();
      void gameOver(QPainter &);      
};
static const int B_WIDTH = 300;
static const int B_HEIGHT = 300;
static const int DOT_SIZE = 10;
static const int ALL_DOTS = 900;
static const int RAND_POS = 29;
static const int DELAY = 140;  

Ý nghĩa của các hằng số trên như sau:

  • static const int B_WIDTH = 300;
    static const int B_HEIGHT = 300;
    static const int DOT_SIZE = 10;
    static const int ALL_DOTS = 900;
    static const int RAND_POS = 29;
    static const int DELAY = 140;  
    
    3 và
    static const int B_WIDTH = 300;
    static const int B_HEIGHT = 300;
    static const int DOT_SIZE = 10;
    static const int ALL_DOTS = 900;
    static const int RAND_POS = 29;
    static const int DELAY = 140;  
    
    4 là kích thước cửa sổ chính.
  • static const int B_WIDTH = 300;
    static const int B_HEIGHT = 300;
    static const int DOT_SIZE = 10;
    static const int ALL_DOTS = 900;
    static const int RAND_POS = 29;
    static const int DELAY = 140;  
    
    5 là kích thước của mồi và mỗi đốt trên con rắn.
  • static const int B_WIDTH = 300;
    static const int B_HEIGHT = 300;
    static const int DOT_SIZE = 10;
    static const int ALL_DOTS = 900;
    static const int RAND_POS = 29;
    static const int DELAY = 140;  
    
    6 là số lượng đốt tối đa của rắn. Vì cửa sổ có kích thước 300 * 300, mỗi đốt rắn có kích thước 10 * 10 nến số lượng đốt tối đa là (300 * 300) / (10 * 10) = 900.
  • static const int B_WIDTH = 300;
    static const int B_HEIGHT = 300;
    static const int DOT_SIZE = 10;
    static const int ALL_DOTS = 900;
    static const int RAND_POS = 29;
    static const int DELAY = 140;  
    
    7 là hằng số để tính vị trí ngẫu nhiên của mồi.
  • static const int B_WIDTH = 300;
    static const int B_HEIGHT = 300;
    static const int DOT_SIZE = 10;
    static const int ALL_DOTS = 900;
    static const int RAND_POS = 29;
    static const int DELAY = 140;  
    
    8 là tốc độ của game.
int x[ALL_DOTS]; 
int y[ALL_DOTS]; 

Hai mảng x, y lưu trữ vị trí của toàn bộ đốt của con rắn.

#include 
#include 
#include "snake.h"

Snake::Snake(QWidget *parent) : QWidget(parent) {

    setStyleSheet("background-color:black;");
    leftDirection = false;
    rightDirection = true;
    upDirection = false;
    downDirection = false;
    inGame = true;

    resize(B_WIDTH, B_HEIGHT);
    loadImages();
    initGame();
}

void Snake::loadImages() {   
    apple.load("apple.png");
    apple = apple.scaled(DOT_SIZE, DOT_SIZE);
}

void Snake::initGame() {

    dots = 3;

    for (int z = 0; z < dots; z++) {
        x[z] = 50 - z * 10;
        y[z] = 50;
    }

    locateApple();

    timerId = startTimer(DELAY);
}
    
void Snake::paintEvent(QPaintEvent *e) {
  
    Q_UNUSED(e);  

    doDrawing();
}    
    
void Snake::doDrawing() {
    
    QPainter qp(this);
    
    if (inGame) {

        qp.drawImage(apple_x, apple_y, apple);

        for (int z = 0; z < dots; z++) { 
             if (z == 0) { 
                 qp.setBrush(QBrush("red")); 
                 qp.drawChord(x[z], y[z], DOT_SIZE, DOT_SIZE, startAngle, spanAngle); 
             } else { 
                 qp.setBrush(QBrush("green")); 
                 qp.drawChord(x[z], y[z], DOT_SIZE, DOT_SIZE, startAngle, spanAngle); 
             } 
        } 
     } else { 
        gameOver(qp); 
     } 
} 

void Snake::gameOver(QPainter &qp) { 
     QString message = "Game over"; 
     QFont font("Courier", 15, QFont::DemiBold); 
     QFontMetrics fm(font); 

     int textWidth = fm.width(message); 
     qp.setFont(font); 
     int h = height(); 
     int w = width(); 

     qp.setPen(QPen(QBrush("white"), 1)); 
     qp.translate(QPoint(w/2, h/2)); 
     qp.drawText(-textWidth/2, 0, message); 
} 

void Snake::checkApple() { 
     if ((x[0] == apple_x) && (y[0] == apple_y)) { 
          dots++; 
          locateApple(); 
     } 
} 

void Snake::move() { 
    for (int z = dots; z > 0; z--) {
        x[z] = x[(z - 1)];
        y[z] = y[(z - 1)];
    }

    if (leftDirection) {
        x[0] -= DOT_SIZE;
    }

    if (rightDirection) {
        x[0] += DOT_SIZE;
    }

    if (upDirection) {
        y[0] -= DOT_SIZE;
    }

    if (downDirection) {
        y[0] += DOT_SIZE;
    }
}

void Snake::checkCollision() {

    for (int z = dots; z > 0; z--) {

        if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
            inGame = false;
        }
    }

    if (y[0] >= B_HEIGHT) {
        inGame = false;
    }

    if (y[0] < 0) { 
        inGame = false; 
    } 

    if (x[0] >= B_WIDTH) {
        inGame = false;
    }

    if (x[0] < 0) { 
        inGame = false; 
    } 

    if(!inGame) { 
        killTimer(timerId); 
    } 
} 

void Snake::locateApple() { 
    QTime time = QTime::currentTime(); 
    qsrand((uint) time.msec()); 
    int r = qrand() % RAND_POS; 
    apple_x = (r * DOT_SIZE); 
    r = qrand() % RAND_POS; 
    apple_y = (r * DOT_SIZE); 
} 

void Snake::timerEvent(QTimerEvent *e) { 
    Q_UNUSED(e); 
    if (inGame) { 
        checkApple(); 
        checkCollision(); 
        move(); 
    } 

    repaint(); 
} 

void Snake::keyPressEvent(QKeyEvent *e) { 
    int key = e->key();
    
    if ((key == Qt::Key_Left) && (!rightDirection)) {
        leftDirection = true;
        upDirection = false;
        downDirection = false;
    }

    if ((key == Qt::Key_Right) && (!leftDirection)) {
        rightDirection = true;
        upDirection = false;
        downDirection = false;
    }

    if ((key == Qt::Key_Up) && (!downDirection)) {
        upDirection = true;
        rightDirection = false;
        leftDirection = false;
    }

    if ((key == Qt::Key_Down) && (!upDirection)) {
        downDirection = true;
        rightDirection = false;
        leftDirection = false;
    }    
    
    QWidget::keyPressEvent(e);    
}
void Snake::loadImages() {
    
    apple.load("apple.png");
    apple = apple.scaled(DOT_SIZE, DOT_SIZE);
}

Phương thức

static const int B_WIDTH = 300;
static const int B_HEIGHT = 300;
static const int DOT_SIZE = 10;
static const int ALL_DOTS = 900;
static const int RAND_POS = 29;
static const int DELAY = 140;  
9load ảnh quả táo dùng làm mồi cho con rắn. Vì ảnh chúng ta dùng có thể có kích thước khác 10*10 nên ta phải resize kích thước ảnh lại bằng phương thức 
int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
0

void Snake::initGame() {

    dots = 3;

    for (int z = 0; z < dots; z++) {
        x[z] = 50 - z * 10;
        y[z] = 50;
    }

    locateApple();

    timerId = startTimer(DELAY);
}

Trong phương thức

int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
1chúng ta cho khởi tạo game, bao gồm khởi tạo số lượng đốt của rắn, khởi tạo vị trí ngẫu nhiên của mồi và chạy timer.

void Snake::checkApple() {

    if ((x[0] == apple_x) && (y[0] == apple_y)) {

        dots++;
        locateApple();
    }
}

Bên trong phương thức

int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
2chúng ta kiểm tra xem nếu vị trí đầu rắn có trùng khớp với vị trí của mồi thì chúng ta tăng số lượng đốt lên và khởi tạo mồi mới.

void Snake::move() {

    for (int z = dots; z > 0; z--) {
        x[z] = x[(z - 1)];
        y[z] = y[(z - 1)];
    }

    if (leftDirection) {
        x[0] -= DOT_SIZE;
    }

    if (rightDirection) {
        x[0] += DOT_SIZE;
    }

    if (upDirection) {
        y[0] -= DOT_SIZE;
    }

    if (downDirection) {
        y[0] += DOT_SIZE;
    }
}

Phương thức

int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
3 cập nhật vị trí mới của con rắn,
int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
4các đốt của con rắn sẽ có vị trí mới là vị trí của đốt phía trước con rắn, đối với đốt đầu tiên thì chúng ta dựa vào hướng di chuyển hiện tại để tính vị trí mới.

Phương thức 

int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
5 kiểm tra xem nếu con rắn có đụng đầu vào tường hay cắn chính mình hay không.

for (int z = dots; z > 0; z--) {

    if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
        inGame = false;
    }
}

Nếu con rắn cắn chính mình thì game over.

if (y[0] >= B_HEIGHT) {
    inGame = false;
}

Nếu con rắn chạm tường thì cũng game over.

static const int B_WIDTH = 300;
static const int B_HEIGHT = 300;
static const int DOT_SIZE = 10;
static const int ALL_DOTS = 900;
static const int RAND_POS = 29;
static const int DELAY = 140;  
0

Phương thức

int x[ALL_DOTS]; 
int y[ALL_DOTS]; 
6 là vòng lặp của game, cứ mỗi lần lặp chúng ta kiểm tra con rắn có ăn mồi, đụng tường hay cắn chính mình hay không và cập nhật vị trí con rắn.

static const int B_WIDTH = 300;
static const int B_HEIGHT = 300;
static const int DOT_SIZE = 10;
static const int ALL_DOTS = 900;
static const int RAND_POS = 29;
static const int DELAY = 140;  
1

Nếu bấm phím mũi tên trái và hướng đi của con rắn không phải hướng ngược lại – tức bên phải thì chúng ta cập nhật lại hướng đi mới của con rắn.