diff options
author | Joel Bodenmann <joel@embedded.pro> | 2016-07-27 15:37:56 +0200 |
---|---|---|
committer | Joel Bodenmann <joel@embedded.pro> | 2016-07-27 15:37:56 +0200 |
commit | 2fe5c112b077d12874eb736bcf9cbf012f3c41ad (patch) | |
tree | 8c85d6c462e6fa39fc43473411c068a9207ae589 /demos/applications/minesweeper/mines.c | |
parent | f2cf8b9814e7c231634860b291ba9a5adab3456f (diff) | |
download | uGFX-2fe5c112b077d12874eb736bcf9cbf012f3c41ad.tar.gz uGFX-2fe5c112b077d12874eb736bcf9cbf012f3c41ad.tar.bz2 uGFX-2fe5c112b077d12874eb736bcf9cbf012f3c41ad.zip |
Adding applications/minesweeper demo
Diffstat (limited to 'demos/applications/minesweeper/mines.c')
-rw-r--r-- | demos/applications/minesweeper/mines.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/demos/applications/minesweeper/mines.c b/demos/applications/minesweeper/mines.c new file mode 100644 index 00000000..55d70984 --- /dev/null +++ b/demos/applications/minesweeper/mines.c @@ -0,0 +1,446 @@ +#include <stdlib.h> +#include "gfx.h" +#include "mines.h" + +typedef struct { // Node properties + uint8_t num; // Node number, how many mines around + bool_t open; // Node shown or hidden + bool_t check; // Node needs to be checked or not, used for opening up empty nodes + bool_t flag; // Node is marked with flag by player + uint16_t fieldNum; // Node number, used to randomize gamestart "animation" +} nodeProps; + +static GEventMouse ev; +static nodeProps minesField[MINES_FIELD_WIDTH][MINES_FIELD_HEIGHT]; // Mines field array +static bool_t minesGameOver = FALSE; +static bool_t minesGameWinner = FALSE; +static int16_t minesEmptyNodes; // Empty node counter +static int16_t minesFlags; // Flag counter +static int16_t minesTime; // Time counter +static GTimer minesTimeCounterTimer; +static const char* minesGraph[] = {"1.bmp","2.bmp","3.bmp","4.bmp","5.bmp","6.bmp","7.bmp","8.bmp", "closed.bmp", "empty.bmp", "explode.bmp", "flag.bmp", "mine.bmp", "wrong.bmp"}; // 14 elements (0-13) +static gdispImage minesImage; +static uint8_t minesStatusIconWidth = 0; +static uint8_t minesStatusIconHeight = 0; +static bool_t minesFirstGame = TRUE; // Just don't clear field for the first time, as we have black screen already... :/ +static bool_t minesSplashTxtVisible = FALSE; +#if MINES_SHOW_SPLASH + static GTimer minesSplashBlink; +#endif + +static int uitoa(unsigned int value, char* buf, int max) +{ + int n = 0; + int i = 0; + int tmp = 0; + + if (!buf) + return -3; + if (2 > max) + return -4; + + i=1; + tmp = value; + if (0 > tmp) { + tmp *= -1; + i++; + } + + for (;;) { + tmp /= 10; + if (0 >= tmp) + break; + i++; + } + if (i >= max) { + buf[0] = '?'; + buf[1] = 0x0; + return 2; + } + + n = i; + tmp = value; + if (0 > tmp) { + tmp *= -1; + } + + buf[i--] = 0x0; + for (;;) { + buf[i--] = (tmp % 10) + '0'; + tmp /= 10; + if (0 >= tmp) + break; + } + + if (-1 != i) { + buf[i--] = '-'; + } + + return n; +} + +static void initRng(void) +{ + srand(gfxSystemTicks()); +} + +static uint32_t randomInt(uint32_t max) +{ + return rand() % max; +} + +static void printStats(void) +{ + char pps_str[12]; + + font_t font = gdispOpenFont("fixed_5x8"); + uitoa(MINES_MINE_COUNT, pps_str, sizeof(pps_str)); + gdispFillString(minesStatusIconWidth+8, gdispGetHeight()-11, " ", font, Black, Black); + gdispDrawString(minesStatusIconWidth+8, gdispGetHeight()-11, pps_str, font, White); + uitoa(minesFlags, pps_str, sizeof(pps_str)); + gdispFillString(8+(minesStatusIconWidth*2)+gdispGetStringWidth("99999", font), gdispGetHeight()-11, " ", font, Black, Black); + gdispDrawString(8+(minesStatusIconWidth*2)+gdispGetStringWidth("99999", font), gdispGetHeight()-11, pps_str, font, White); + gdispCloseFont(font); +} + +static void minesUpdateTime(void) +{ + char pps_str[12]; + + if (minesTime > 9999) + minesTime = 9999; + + font_t font = gdispOpenFont("digital_7__mono_20"); + uitoa(minesTime, pps_str, sizeof(pps_str)); + gdispFillArea((MINES_FIELD_WIDTH*MINES_CELL_WIDTH)-gdispGetStringWidth("9999", font), gdispGetHeight()-15, gdispGetWidth(), 15, Black); + gdispDrawString((MINES_FIELD_WIDTH*MINES_CELL_WIDTH)-gdispGetStringWidth(pps_str, font), gdispGetHeight()-15, pps_str, font, Lime); + gdispCloseFont(font); +} + +static void minesTimeCounter(void* arg) +{ + (void)arg; + + minesTime++; + minesUpdateTime(); +} + +static bool_t inRange(int16_t x, int16_t y) +{ + if ((x >= 0) && (x < MINES_FIELD_WIDTH) && (y >= 0) && (y < MINES_FIELD_HEIGHT)) + return TRUE; + else + return FALSE; +} + +static void showOne(int16_t x, int16_t y) +{ + minesField[x][y].open = TRUE; + if (minesField[x][y].flag) { + minesField[x][y].flag = FALSE; + minesFlags--; + } + + gdispFillArea((x*MINES_CELL_WIDTH)+1, (y*MINES_CELL_HEIGHT)+1, MINES_CELL_WIDTH-1, MINES_CELL_HEIGHT-1, Black); + + if ((minesField[x][y].num > 0) && (minesField[x][y].num < 9)) { + gdispImageOpenFile(&minesImage, minesGraph[minesField[x][y].num-1]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gdispImageClose(&minesImage); + minesEmptyNodes--; + } else if (minesField[x][y].num == 9) { + minesGameOver = TRUE; + minesGameWinner = FALSE; + gdispImageOpenFile(&minesImage, minesGraph[10]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gdispImageClose(&minesImage); + // Dirty HACK to not draw mine icon on GameOver event :D + minesField[x][y].num = 0; + } else if (minesField[x][y].num == 0) { + gdispImageOpenFile(&minesImage, minesGraph[9]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gdispImageClose(&minesImage); + minesField[x][y].check = TRUE; + minesEmptyNodes--; + } +} + +static void openEmptyNodes(void) +{ + int16_t x, y, i, j; + bool_t needToCheck = TRUE; + + while (needToCheck) { + needToCheck = FALSE; + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + if (minesField[x][y].check) { + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + if ((i != 0) || (j != 0)) { // We don't need to check middle node as it is the one we are checking right now! :D + if (inRange(x+i,y+j)) { + if (!minesField[x+i][y+j].open) showOne(x+i,y+j); + if (minesField[x+i][y+j].check) needToCheck = TRUE; + } + } + } + } + minesField[x][y].check = FALSE; + } + } + } + } +} + +static DECLARE_THREAD_FUNCTION(thdMines, msg) +{ + (void)msg; + uint16_t x,y, delay; + bool_t delayed = FALSE; + while (!minesGameOver) { + if (minesEmptyNodes == 0) { + minesGameOver = TRUE; + minesGameWinner = TRUE; + } + initRng(); + ginputGetMouseStatus(0, &ev); + delayed = FALSE; + if (ev.buttons & GINPUT_MOUSE_BTN_LEFT) { + x = ev.x/MINES_CELL_WIDTH; + y = ev.y/MINES_CELL_WIDTH; + delay = 0; + while (ev.buttons & GINPUT_MOUSE_BTN_LEFT) { // Wait until release + ginputGetMouseStatus(0, &ev); + gfxSleepMilliseconds(1); + delay++; + if (delay >= MINES_FLAG_DELAY) { + delay = MINES_FLAG_DELAY; + if (!delayed && inRange(x, y) && !minesField[x][y].open) { + if (minesField[x][y].flag) { + gdispImageOpenFile(&minesImage, minesGraph[8]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH-1, MINES_CELL_HEIGHT-1, 0, 0); + gdispImageClose(&minesImage); + minesField[x][y].flag = FALSE; + minesFlags--; + printStats(); + } else { + gdispImageOpenFile(&minesImage, minesGraph[11]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gdispImageClose(&minesImage); + minesField[x][y].flag = TRUE; + minesFlags++; + printStats(); + } + delayed = TRUE; + } + } + } + // Check time, if longer than MINES_FLAG_DELAY then add flag... + if (delay < MINES_FLAG_DELAY) { + if ((x < MINES_FIELD_WIDTH) && (y < MINES_FIELD_HEIGHT) && !minesField[x][y].open && !minesField[x][y].flag) { + showOne(x, y); + openEmptyNodes(); + printStats(); + } + } + } + } + THREAD_RETURN(0); +} + +static void printGameOver(void) +{ + if (minesGameOver) { + font_t font = gdispOpenFont("DejaVuSans16"); + if (minesGameWinner) { + gdispDrawString((gdispGetWidth()-gdispGetStringWidth("You LIVE!", font))/2, gdispGetHeight()-15, "You LIVE!", font, White); + } else { + gdispDrawString((gdispGetWidth()-gdispGetStringWidth("You DIED!", font))/2, gdispGetHeight()-15, "You DIED!", font, White); + } + gdispCloseFont(font); + } else { + gdispFillArea(0, gdispGetHeight()-25, gdispGetWidth(), 25, Black); + } +} + +static void initField(void) +{ + int16_t x, y, mines, i, j; + + minesFlags = 0; + minesGameOver = FALSE; + printGameOver(); + + font_t font = gdispOpenFont("fixed_5x8"); + gdispImageOpenFile(&minesImage, "plainmine.bmp"); + // Saving status icons width/height for later use + minesStatusIconWidth = minesImage.width; + minesStatusIconHeight = minesImage.height; + gdispImageDraw(&minesImage, 4, gdispGetHeight()-minesImage.height, minesImage.width, minesImage.height, 0, 0); + gdispImageClose(&minesImage); + gdispImageOpenFile(&minesImage, "plainflag.bmp"); + gdispImageDraw(&minesImage, 4+minesImage.width+gdispGetStringWidth("99999", font), gdispGetHeight()-minesImage.height, minesImage.width, minesImage.height, 0, 0); + gdispImageClose(&minesImage); + gdispCloseFont(font); + printStats(); + + initRng(); + + // Clearing/resetting field here... + i = 0; + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + minesField[x][y].num = 0; + minesField[x][y].open = FALSE; + minesField[x][y].check = FALSE; + minesField[x][y].flag = FALSE; + minesField[x][y].fieldNum = i; + i++; + } + } + + // Randomizing closed field drawing... + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + // Getting random node and swapping it with current + i = randomInt(MINES_FIELD_WIDTH); + j = randomInt(MINES_FIELD_HEIGHT); + mines = minesField[x][y].fieldNum; + minesField[x][y].fieldNum = minesField[i][j].fieldNum; + minesField[i][j].fieldNum = mines; + } + } + + // Clearing nodes randomly + if (!minesFirstGame) { + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + i = minesField[x][y].fieldNum/MINES_FIELD_HEIGHT; + j = minesField[x][y].fieldNum-(i*MINES_FIELD_HEIGHT); + gdispFillArea((i*MINES_CELL_WIDTH)+1, (j*MINES_CELL_HEIGHT)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, Black); + gfxSleepMilliseconds(2); + } + } + } else { + minesFirstGame = FALSE; + } + + // Drawing closed nodes randomly + gdispImageOpenFile(&minesImage, minesGraph[8]); + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + i = minesField[x][y].fieldNum/MINES_FIELD_HEIGHT; + j = minesField[x][y].fieldNum-(i*MINES_FIELD_HEIGHT); + gdispImageDraw(&minesImage, (i*MINES_CELL_HEIGHT)+1, (j*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gfxSleepMilliseconds(2); + } + } + gdispImageClose(&minesImage); + minesEmptyNodes = MINES_FIELD_WIDTH*MINES_FIELD_HEIGHT; + + // Placing mines in random nodes :D + mines = 0; + while (mines != MINES_MINE_COUNT) { + x = randomInt(MINES_FIELD_WIDTH); + y = randomInt(MINES_FIELD_HEIGHT); + if (minesField[x][y].num != 9) { + mines++; + minesEmptyNodes--; + minesField[x][y].num = 9; + } + } + + // Calculating numbers for nearby mine nodes + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + if (minesField[x][y].num != 9) { + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + if ((i != 0) || (j != 0)) { // We don't need to check middle node as we already know it is not a mine! :D + if (inRange(x+i,y+j) && (minesField[x+i][y+j].num == 9)) { + minesField[x][y].num++; + } + } + } + } + } + } + } + + minesTime = 0; + minesUpdateTime(); + gtimerStart(&minesTimeCounterTimer, minesTimeCounter, 0, TRUE, 1000); +} + +void minesStart(void) +{ + int16_t x, y; + +#if MINES_SHOW_SPLASH + gtimerStop(&minesSplashBlink); + gdispClear(Black); +#endif + + initField(); + gfxThreadCreate(0, 1024, NORMAL_PRIORITY, thdMines, 0); + while (!minesGameOver) { + gfxSleepMilliseconds(100); + } + printGameOver(); + gtimerStop(&minesTimeCounterTimer); + + if (!minesGameWinner) { + // Print generated mines for player to see + font_t font = gdispOpenFont("fixed_10x20"); + for (x = 0; x < MINES_FIELD_WIDTH; x++) { + for (y = 0; y < MINES_FIELD_HEIGHT; y++) { + if (minesField[x][y].num == 9 && !minesField[x][y].flag) { + gdispImageOpenFile(&minesImage, minesGraph[12]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gdispImageClose(&minesImage); + } + if (minesField[x][y].flag && (minesField[x][y].num != 9)) { + gdispImageOpenFile(&minesImage, minesGraph[13]); + gdispImageDraw(&minesImage, (x*MINES_CELL_HEIGHT)+1, (y*MINES_CELL_WIDTH)+1, MINES_CELL_WIDTH, MINES_CELL_HEIGHT, 0, 0); + gdispImageClose(&minesImage); + } + } + } + gdispCloseFont(font); + } +} + +#if MINES_SHOW_SPLASH + static void minesSplashBlinker(void* arg) + { + (void)arg; + + minesSplashTxtVisible = !minesSplashTxtVisible; + + if (minesSplashTxtVisible) { + gdispImageOpenFile(&minesImage, "splashtxt.bmp"); + } else { + gdispImageOpenFile(&minesImage, "splashclr.bmp"); + } + + gdispImageDraw(&minesImage, (gdispGetWidth()/2)-150+93, (gdispGetHeight()/2)-100+161, 112, 10, 0, 0); + + gdispImageClose(&minesImage); + } + + void minesShowSplash(void) + { + gdispImageOpenFile(&minesImage, "splash.bmp"); + gdispImageDraw(&minesImage, (gdispGetWidth()/2)-150, (gdispGetHeight()/2)-100, 300, 200, 0, 0); + gdispImageClose(&minesImage); + + gtimerStart(&minesSplashBlink, minesSplashBlinker, 0, TRUE, 400); + } +#endif + +void minesInit(void) +{ + initRng(); + + gdispClear(Black); +} |