#include "main.h" // HAL, ILI9341, CMSIS-RTOS handles #include "cmsis_os.h" // RTOS types like osMutexAcquire, osMessageQueueGet #include "stdlib.h" // For rand() #include "stdio.h" // For sprintf() #include "snake.h" // Your own header for declarations #include "ILI9341_STM32_Driver.h" #include "fonts.h" struct Snake snake; // define the global variable here #define FLASH_USER_ADDR ((uint32_t)0x0803F000) // Last 4 KB void Flash_Write_HighScore(uint32_t highscore) { HAL_FLASH_Unlock(); // Erase the sector first FLASH_EraseInitTypeDef EraseInitStruct; uint32_t PageError = 0; EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; EraseInitStruct.Sector = FLASH_SECTOR_5; EraseInitStruct.NbSectors = 1; if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK) { // Handle error return; } // Program the word if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_USER_ADDR, highscore) != HAL_OK) { // Handle error } HAL_FLASH_Lock(); } uint32_t Flash_Read_HighScore(void) { return *(uint32_t*) FLASH_USER_ADDR; } // Function to initialize game variables void GameInit() { snake.gamesizeheight = 6; snake.gamesizewidth = 8; snake.isGameOver = 0; snake.Dir = RIGHT; snake.x = 1; //snake.gamesizewidth / 2; snake.y = 1; //snake.gamesizeheight / 2; snake.fruitCordX = rand() % snake.gamesizewidth; snake.fruitCordY = rand() % snake.gamesizeheight; snake.playerScore = 0; snake.TailLen = 0; snake.TailGrowPending = 0; if (osMutexAcquire(displayHandle, osWaitForever) == osOK) { ILI9341_FillScreen(WHITE); osMutexRelease(displayHandle); } } // Function for updating the game state void UpdateGame() { char dir; // check the queue non blocking osStatus_t status = osMessageQueueGet(pressesHandle, &dir, NULL, 0); // 0 = no wait if (status == osOK) { switch (dir) { case 'L': snake.Dir = LEFT; break; case 'R': snake.Dir = RIGHT; break; case 'U': snake.Dir = UP; break; case 'D': snake.Dir = DOWN; break; } } if (!snake.TailGrowPending) { if (snake.TailLen > 0) { snake.TailPendingDeletionX = snake.TailX[snake.TailLen - 1]; snake.TailPendingDeletionY = snake.TailY[snake.TailLen - 1]; } else { snake.TailPendingDeletionX = snake.x; snake.TailPendingDeletionY = snake.y; } } snake.TailGrowPending = 0; // tail movement if (snake.TailLen > 0) { for (int i = snake.TailLen - 1; i > 0; i--) { snake.TailX[i] = snake.TailX[i - 1]; snake.TailY[i] = snake.TailY[i - 1]; } snake.TailX[0] = snake.x; snake.TailY[0] = snake.y; } switch (snake.Dir) { case LEFT: snake.x--; break; case RIGHT: snake.x++; break; case UP: snake.y--; break; case DOWN: snake.y++; break; } // Checks for snake's collision with the wall if (snake.x >= snake.gamesizewidth || snake.x < 0 || snake.y >= snake.gamesizeheight || snake.y < 0) snake.isGameOver = 1; // Checks for collision with the tail (o) for (int i = 0; i < snake.TailLen; i++) { if (snake.TailX[i] == snake.x && snake.TailY[i] == snake.y) snake.isGameOver = 1; } // Checks for snake's collision with the food (#) if (snake.x == snake.fruitCordX && snake.y == snake.fruitCordY) { snake.playerScore += 10; snake.fruitCordX = rand() % snake.gamesizewidth; snake.fruitCordY = rand() % snake.gamesizeheight; snake.TailLen++; snake.TailX[snake.TailLen - 1] = snake.x; snake.TailY[snake.TailLen - 1] = snake.y; snake.TailGrowPending = 1; //snake.TailPendingDeletionX = -1; } if (snake.isGameOver) { if (osMutexAcquire(displayHandle, osWaitForever) == osOK) { if (Flash_Read_HighScore() > snake.playerScore) { char str[50]; sprintf(str, "Highscore Remains: %lu", Flash_Read_HighScore()); ILI9341_DrawText(str, FONT3, 320 / 2 - 70, 240 / 2, BLACK, WHITE); // ILI9341_WriteString(320 / 2 - 70, 240 / 2, str, Font_11x18, //ILI9341_MAGENTA, ILI9341_WHITE); } else { char str[50]; sprintf(str, "New Highscore: %lu", snake.playerScore); //ILI9341_WriteString(320 / 2 - 70, 240 / 2, str, Font_11x18, //ILI9341_MAGENTA, ILI9341_WHITE); ILI9341_DrawText(str, FONT3, 320 / 2 - 70, 240 / 2, BLACK, WHITE); Flash_Write_HighScore(snake.playerScore); } osMutexRelease(displayHandle); } } } // Function for creating the game board & rendering void GameRender() { // Creating top walls // Creating side walls //ILI9341_FillRectangle(j, i, ILI9341_WIDTH/snake.gamesizewidth, ILI9341_HEIGHT/snake.gamesizeheight, ILI9341_YELLOW); //ILI9341_FillScreen(MAGENTA); for (int x = 0; x < snake.gamesizewidth; x++) { for (int y = 0; y <= snake.gamesizeheight; y++) { if (x == snake.x && y == snake.y) { if (osMutexAcquire(displayHandle, osWaitForever) == osOK) { ILI9341_DrawFilledRectangleCoord( (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight, (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth + ILI9341_SCREEN_WIDTH / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight + ILI9341_SCREEN_HEIGHT / snake.gamesizeheight, BLACK); // ILI9341_FillRectangle( // (x * ILI9341_WIDTH) / snake.gamesizewidth, // (y * ILI9341_HEIGHT) / snake.gamesizeheight, // ILI9341_WIDTH / snake.gamesizewidth, // ILI9341_HEIGHT / snake.gamesizeheight, // ILI9341_BLACK); osMutexRelease(displayHandle); } } //cout << "O"; // Creating the sanke's food with '#' else if (x == snake.fruitCordX && y == snake.fruitCordY) { if (osMutexAcquire(displayHandle, osWaitForever) == osOK) { ILI9341_DrawFilledRectangleCoord( (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight, (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth + ILI9341_SCREEN_WIDTH / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight + ILI9341_SCREEN_HEIGHT / snake.gamesizeheight, RED); // ILI9341_FillRectangle( // (x * ILI9341_WIDTH) / snake.gamesizewidth, // (y * ILI9341_HEIGHT) / snake.gamesizeheight, // ILI9341_WIDTH / snake.gamesizewidth, // ILI9341_HEIGHT / snake.gamesizeheight, ILI9341_RED); osMutexRelease(displayHandle); } } //cout << "#"; else if (snake.TailLen >= 0) { for (int i = 0; i < snake.TailLen; i++) { if (snake.TailX[0] == x && snake.TailY[0] == y) { if (osMutexAcquire(displayHandle, osWaitForever) == osOK) { ILI9341_DrawFilledRectangleCoord( (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight, (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth + ILI9341_SCREEN_WIDTH / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight + ILI9341_SCREEN_HEIGHT / snake.gamesizeheight, GREEN); // ILI9341_FillRectangle( // (x * ILI9341_WIDTH) / snake.gamesizewidth, // (y * ILI9341_HEIGHT) / snake.gamesizeheight, // ILI9341_WIDTH / snake.gamesizewidth, // ILI9341_HEIGHT / snake.gamesizeheight, // ILI9341_GREEN); osMutexRelease(displayHandle); } } } } if (snake.TailPendingDeletionX == x && snake.TailPendingDeletionY == y) { if (osMutexAcquire(displayHandle, osWaitForever) == osOK) { ILI9341_DrawFilledRectangleCoord( (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight, (x * ILI9341_SCREEN_WIDTH) / snake.gamesizewidth + ILI9341_SCREEN_WIDTH / snake.gamesizewidth, (y * ILI9341_SCREEN_HEIGHT) / snake.gamesizeheight + ILI9341_SCREEN_HEIGHT / snake.gamesizeheight, WHITE); // ILI9341_FillRectangle( // (x * ILI9341_WIDTH) / snake.gamesizewidth, // (y * ILI9341_HEIGHT) / snake.gamesizeheight, // ILI9341_WIDTH / snake.gamesizewidth, // ILI9341_HEIGHT / snake.gamesizeheight, // ILI9341_WHITE); osMutexRelease(displayHandle); } } } } } int isGameOver(){ return snake.isGameOver; }