2016-07-27 13:37:56 +00:00
# include <stdlib.h>
# include "gfx.h"
# include "mines.h"
typedef struct { // Node properties
uint8_t num ; // Node number, how many mines around
2018-06-23 03:02:07 +00:00
gBool open ; // Node shown or hidden
gBool check ; // Node needs to be checked or not, used for opening up empty nodes
gBool flag ; // Node is marked with flag by player
2016-07-27 13:37:56 +00:00
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
2018-06-23 03:02:07 +00:00
static gBool minesGameOver = gFalse ;
static gBool minesGameWinner = gFalse ;
2016-07-27 13:37:56 +00:00
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 ;
2018-06-23 03:02:07 +00:00
static gBool minesFirstGame = gTrue ; // Just don't clear field for the first time, as we have black screen already... :/
static gBool minesSplashTxtVisible = gFalse ;
2016-07-27 13:37:56 +00:00
# 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 ] ;
2018-07-08 03:05:27 +00:00
gFont font = gdispOpenFont ( " fixed_5x8 " ) ;
2016-07-27 13:37:56 +00:00
uitoa ( MINES_MINE_COUNT , pps_str , sizeof ( pps_str ) ) ;
2018-03-10 10:36:12 +00:00
gdispFillString ( minesStatusIconWidth + 8 , gdispGetHeight ( ) - 11 , " " , font , GFX_BLACK , GFX_BLACK ) ;
gdispDrawString ( minesStatusIconWidth + 8 , gdispGetHeight ( ) - 11 , pps_str , font , GFX_WHITE ) ;
2016-07-27 13:37:56 +00:00
uitoa ( minesFlags , pps_str , sizeof ( pps_str ) ) ;
2018-03-10 10:36:12 +00:00
gdispFillString ( 8 + ( minesStatusIconWidth * 2 ) + gdispGetStringWidth ( " 99999 " , font ) , gdispGetHeight ( ) - 11 , " " , font , GFX_BLACK , GFX_BLACK ) ;
gdispDrawString ( 8 + ( minesStatusIconWidth * 2 ) + gdispGetStringWidth ( " 99999 " , font ) , gdispGetHeight ( ) - 11 , pps_str , font , GFX_WHITE ) ;
2016-07-27 13:37:56 +00:00
gdispCloseFont ( font ) ;
}
static void minesUpdateTime ( void )
{
char pps_str [ 12 ] ;
if ( minesTime > 9999 )
minesTime = 9999 ;
2018-07-08 03:05:27 +00:00
gFont font = gdispOpenFont ( " digital_7__mono_20 " ) ;
2016-07-27 13:37:56 +00:00
uitoa ( minesTime , pps_str , sizeof ( pps_str ) ) ;
2018-03-10 10:36:12 +00:00
gdispFillArea ( ( MINES_FIELD_WIDTH * MINES_CELL_WIDTH ) - gdispGetStringWidth ( " 9999 " , font ) , gdispGetHeight ( ) - 15 , gdispGetWidth ( ) , 15 , GFX_BLACK ) ;
gdispDrawString ( ( MINES_FIELD_WIDTH * MINES_CELL_WIDTH ) - gdispGetStringWidth ( pps_str , font ) , gdispGetHeight ( ) - 15 , pps_str , font , GFX_LIME ) ;
2016-07-27 13:37:56 +00:00
gdispCloseFont ( font ) ;
}
static void minesTimeCounter ( void * arg )
{
( void ) arg ;
minesTime + + ;
minesUpdateTime ( ) ;
}
2018-06-23 03:02:07 +00:00
static gBool inRange ( int16_t x , int16_t y )
2016-07-27 13:37:56 +00:00
{
if ( ( x > = 0 ) & & ( x < MINES_FIELD_WIDTH ) & & ( y > = 0 ) & & ( y < MINES_FIELD_HEIGHT ) )
2018-06-23 03:02:07 +00:00
return gTrue ;
2016-07-27 13:37:56 +00:00
else
2018-06-23 03:02:07 +00:00
return gFalse ;
2016-07-27 13:37:56 +00:00
}
static void showOne ( int16_t x , int16_t y )
{
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . open = gTrue ;
2016-07-27 13:37:56 +00:00
if ( minesField [ x ] [ y ] . flag ) {
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . flag = gFalse ;
2016-07-27 13:37:56 +00:00
minesFlags - - ;
}
2018-03-10 10:36:12 +00:00
gdispFillArea ( ( x * MINES_CELL_WIDTH ) + 1 , ( y * MINES_CELL_HEIGHT ) + 1 , MINES_CELL_WIDTH - 1 , MINES_CELL_HEIGHT - 1 , GFX_BLACK ) ;
2016-07-27 13:37:56 +00:00
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 ) {
2018-06-23 03:02:07 +00:00
minesGameOver = gTrue ;
minesGameWinner = gFalse ;
2016-07-27 13:37:56 +00:00
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 ) ;
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . check = gTrue ;
2016-07-27 13:37:56 +00:00
minesEmptyNodes - - ;
}
}
static void openEmptyNodes ( void )
{
int16_t x , y , i , j ;
2018-06-23 03:02:07 +00:00
gBool needToCheck = gTrue ;
2016-07-27 13:37:56 +00:00
while ( needToCheck ) {
2018-06-23 03:02:07 +00:00
needToCheck = gFalse ;
2016-07-27 13:37:56 +00:00
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 ) ;
2018-06-23 03:02:07 +00:00
if ( minesField [ x + i ] [ y + j ] . check ) needToCheck = gTrue ;
2016-07-27 13:37:56 +00:00
}
}
}
}
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . check = gFalse ;
2016-07-27 13:37:56 +00:00
}
}
}
}
}
static DECLARE_THREAD_FUNCTION ( thdMines , msg )
{
( void ) msg ;
uint16_t x , y , delay ;
2018-06-23 03:02:07 +00:00
gBool delayed = gFalse ;
2016-07-27 13:37:56 +00:00
while ( ! minesGameOver ) {
if ( minesEmptyNodes = = 0 ) {
2018-06-23 03:02:07 +00:00
minesGameOver = gTrue ;
minesGameWinner = gTrue ;
2016-07-27 13:37:56 +00:00
}
initRng ( ) ;
ginputGetMouseStatus ( 0 , & ev ) ;
2018-06-23 03:02:07 +00:00
delayed = gFalse ;
2016-07-27 13:37:56 +00:00
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 ) ;
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . flag = gFalse ;
2016-07-27 13:37:56 +00:00
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 ) ;
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . flag = gTrue ;
2016-07-27 13:37:56 +00:00
minesFlags + + ;
printStats ( ) ;
}
2018-06-23 03:02:07 +00:00
delayed = gTrue ;
2016-07-27 13:37:56 +00:00
}
}
}
// 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 ) {
2018-07-08 03:05:27 +00:00
gFont font = gdispOpenFont ( " DejaVuSans16 " ) ;
2016-07-27 13:37:56 +00:00
if ( minesGameWinner ) {
2018-03-10 10:36:12 +00:00
gdispDrawString ( ( gdispGetWidth ( ) - gdispGetStringWidth ( " You LIVE! " , font ) ) / 2 , gdispGetHeight ( ) - 15 , " You LIVE! " , font , GFX_WHITE ) ;
2016-07-27 13:37:56 +00:00
} else {
2018-03-10 10:36:12 +00:00
gdispDrawString ( ( gdispGetWidth ( ) - gdispGetStringWidth ( " You DIED! " , font ) ) / 2 , gdispGetHeight ( ) - 15 , " You DIED! " , font , GFX_WHITE ) ;
2016-07-27 13:37:56 +00:00
}
gdispCloseFont ( font ) ;
} else {
2018-03-10 10:36:12 +00:00
gdispFillArea ( 0 , gdispGetHeight ( ) - 25 , gdispGetWidth ( ) , 25 , GFX_BLACK ) ;
2016-07-27 13:37:56 +00:00
}
}
static void initField ( void )
{
int16_t x , y , mines , i , j ;
minesFlags = 0 ;
2018-06-23 03:02:07 +00:00
minesGameOver = gFalse ;
2016-07-27 13:37:56 +00:00
printGameOver ( ) ;
2018-07-08 03:05:27 +00:00
gFont font = gdispOpenFont ( " fixed_5x8 " ) ;
2016-07-27 13:37:56 +00:00
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 ;
2018-06-23 03:02:07 +00:00
minesField [ x ] [ y ] . open = gFalse ;
minesField [ x ] [ y ] . check = gFalse ;
minesField [ x ] [ y ] . flag = gFalse ;
2016-07-27 13:37:56 +00:00
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 ) ;
2018-03-10 10:36:12 +00:00
gdispFillArea ( ( i * MINES_CELL_WIDTH ) + 1 , ( j * MINES_CELL_HEIGHT ) + 1 , MINES_CELL_WIDTH , MINES_CELL_HEIGHT , GFX_BLACK ) ;
2016-07-27 13:37:56 +00:00
gfxSleepMilliseconds ( 2 ) ;
}
}
} else {
2018-06-23 03:02:07 +00:00
minesFirstGame = gFalse ;
2016-07-27 13:37:56 +00:00
}
// 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 ( ) ;
2018-06-23 03:02:07 +00:00
gtimerStart ( & minesTimeCounterTimer , minesTimeCounter , 0 , gTrue , 1000 ) ;
2016-07-27 13:37:56 +00:00
}
void minesStart ( void )
{
int16_t x , y ;
# if MINES_SHOW_SPLASH
gtimerStop ( & minesSplashBlink ) ;
2018-03-10 10:36:12 +00:00
gdispClear ( GFX_BLACK ) ;
2016-07-27 13:37:56 +00:00
# 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
2018-07-08 03:05:27 +00:00
gFont font = gdispOpenFont ( " fixed_10x20 " ) ;
2016-07-27 13:37:56 +00:00
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 ) ;
2018-06-23 03:02:07 +00:00
gtimerStart ( & minesSplashBlink , minesSplashBlinker , 0 , gTrue , 400 ) ;
2016-07-27 13:37:56 +00:00
}
# endif
void minesInit ( void )
{
initRng ( ) ;
2018-03-10 10:36:12 +00:00
gdispClear ( GFX_BLACK ) ;
2016-07-27 13:37:56 +00:00
}