123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /******************************************************************************
- * M5Snake : Game board management *
- * ------------------------------- *
- * Manage the game board (storage in memory and display *
- * Author: Olivier Staquet *
- * Last version available on https://github.com/ostaquet/M5Snake *
- *****************************************************************************/
- #include "GameBoard.h"
- /**
- * Initialize
- */
- void GameBoardClass::begin(uint8_t _max_game_cycles) {
- // Keep the number of game cycles
- max_game_cycles = _max_game_cycles;
- current_game_cycle = 0;
- // Init the board at blank
- for (uint8_t x = 0; x < board_width; x++) {
- for (uint8_t y = 0; y < board_height; y++) {
- board_data[x][y] = BLOCK_STATUS_EMPTY;
- board_changes[x][y] = 0;
- }
- }
- // Set screen to blank
- M5.Lcd.fillScreen(BLACK);
- }
- /**
- * Refresh display
- */
- void GameBoardClass::refresh() {
- // Check where there are some changes
- for (uint8_t x = 0; x < board_width; x++) {
- for (uint8_t y = 0; y < board_height; y++) {
- // Check the cell
- if (board_changes[x][y] > 0) {
- // There is a change...
- drawChange(x, y);
- // Reset the change tracking
- board_changes[x][y] = 0;
- }
- }
- }
- }
- /**
- * Put the head of the snake on the board and go on right
- */
- void GameBoardClass::startSnake() {
- // Define the middle of the screen
- setCell(board_width / 2, board_height / 2, BLOCK_STATUS_HEAD);
- // Define the direction
- setDirection(DIRECTION_RIGHT);
- }
- /**
- * Make the snake move on the board
- * Return boolean true if OK, false if game over
- */
- bool GameBoardClass::moveSnake() {
- // Check if it is a cycle to move
- if (current_game_cycle < max_game_cycles) {
- // Wait for the next cycle
- current_game_cycle++;
- return true;
- } else {
- // Reset the game cycle
- current_game_cycle = 0;
- }
- // Add 1 to all current block with between 1 and 512
- // to keep count of the movement of the snake (1 = head, 2 = 2nd block after
- // head...)
- for (uint8_t x = 0; x < board_width; x++) {
- for (uint8_t y = 0; y < board_height; y++) {
- if (board_data[x][y] < BLOCK_STATUS_CHERRY &&
- board_data[x][y] != BLOCK_STATUS_EMPTY) {
- board_data[x][y] = board_data[x][y] + 1;
- }
- }
- }
- // Next move to be defined
- int8_t next_block_x = current_head_x;
- int8_t next_block_y = current_head_y;
- // Define the next move of the head
- switch (current_direction) {
- case DIRECTION_UP:
- next_block_y = current_head_y - 1;
- break;
- case DIRECTION_RIGHT:
- next_block_x = current_head_x + 1;
- break;
- case DIRECTION_DOWN:
- next_block_y = current_head_y + 1;
- break;
- case DIRECTION_LEFT:
- next_block_x = current_head_x - 1;
- break;
- }
- // Check if the move is valid...
- // Check the limit of the board for X
- if (next_block_x < 0 || next_block_x >= board_width) {
- return false;
- }
- // Check the limit of the board for Y
- if (next_block_y < 0 || next_block_y >= board_height) {
- return false;
- }
- // Check if there is a cherry on the cell (if not a cherry, remove the last
- // block of the tail
- if (board_data[next_block_x][next_block_y] != BLOCK_STATUS_CHERRY) {
- removeTail();
- }
- // Check if there is another part of the snake
- if (board_data[next_block_x][next_block_y] > BLOCK_STATUS_EMPTY &&
- board_data[next_block_x][next_block_y] < BLOCK_STATUS_CHERRY) {
- return false;
- }
- // OK, move the head of the snake
- setCell(next_block_x, next_block_y, BLOCK_STATUS_HEAD);
- return true;
- }
- /**
- * Identify and remove the tail (last block of the snake)
- */
- void GameBoardClass::removeTail() {
- uint16_t greatest_value = 0;
- uint8_t tail_x = 0;
- uint8_t tail_y = 0;
- // Find the cell with the biggest value (it is the tail)
- for (uint8_t x = 0; x < board_width; x++) {
- for (uint8_t y = 0; y < board_height; y++) {
- if (board_data[x][y] < BLOCK_STATUS_CHERRY) {
- if (board_data[x][y] > greatest_value) {
- tail_x = x;
- tail_y = y;
- greatest_value = board_data[x][y];
- }
- }
- }
- }
- // Remove the tail
- setCell(tail_x, tail_y, BLOCK_STATUS_EMPTY);
- }
- /**
- * Get the max score
- */
- uint16_t GameBoardClass::getMaxScore() {
- uint16_t greatest_value = 0;
- // Find the cell with the biggest value (it is the tail)
- for (uint8_t x = 0; x < board_width; x++) {
- for (uint8_t y = 0; y < board_height; y++) {
- if (board_data[x][y] < BLOCK_STATUS_CHERRY) {
- if (board_data[x][y] > greatest_value) {
- greatest_value = board_data[x][y];
- }
- }
- }
- }
- return greatest_value - 1;
- }
- /**
- * Draw the change of one cell
- */
- void GameBoardClass::drawChange(uint8_t x, uint8_t y) {
- // Compute box position
- uint16_t pos_x = x * BLOCK_SIZE;
- uint16_t pos_y = y * BLOCK_SIZE;
- // Depending on the content of the cell, draw the box
- switch (board_data[x][y]) {
- case BLOCK_STATUS_EMPTY:
- M5.Lcd.fillRect(pos_x, pos_y, BLOCK_SIZE, BLOCK_SIZE, BLACK);
- break;
- case BLOCK_STATUS_CHERRY:
- M5.Lcd.fillRect(pos_x, pos_y, BLOCK_SIZE, BLOCK_SIZE, BLACK);
- M5.Lcd.fillCircle(pos_x + BLOCK_SIZE / 2, pos_y + BLOCK_SIZE / 2,
- BLOCK_SIZE / 2 - 1, RED);
- break;
- default:
- M5.Lcd.drawRect(pos_x, pos_y, BLOCK_SIZE, BLOCK_SIZE, BLACK);
- M5.Lcd.fillRect(pos_x + 1, pos_y + 1, BLOCK_SIZE - 2,
- BLOCK_SIZE - 2, WHITE);
- break;
- }
- }
- /**
- * Add a ramdom cherry on the board
- */
- void GameBoardClass::addCherry() {
- uint8_t pos_x = random(0, board_width);
- uint8_t pos_y = random(0, board_height);
- while (board_data[pos_x][pos_y] != BLOCK_STATUS_EMPTY) {
- pos_x = random(0, board_width);
- pos_y = random(0, board_height);
- }
- setCell(pos_x, pos_y, BLOCK_STATUS_CHERRY);
- }
- /**
- * Set direction
- */
- void GameBoardClass::setDirection(uint8_t direction) {
- current_direction = direction;
- }
- /**
- * Set a value in a cell
- */
- void GameBoardClass::setCell(uint8_t x, uint8_t y, uint16_t status) {
- board_data[x][y] = status;
- board_changes[x][y] = 1;
- if (status == BLOCK_STATUS_HEAD) {
- current_head_x = x;
- current_head_y = y;
- }
- }
- GameBoardClass GameBoard;
|