rockbox/apps/plugins/minesweeper.c
Simon Rothen 0b5ad60c26 Introducing Targets iBasso DX50 & iBasso DX90
The port to for this two targets has been entirely developped by Ilia Sergachev (alias Il or xzcc). His source
can be found at https://bitbucket.org/isergachev/rockbox . The few necesary modifications for the DX90 port
was done by headwhacker form head-fi.org. Unfortunately i could not try out the final state of the DX90 port.
The port is hosted on android (without java) as standalone app. The official Firmware is required to run this port.
Ilia did modify the source files for the "android" target in the rockbox source to make the DX port work. The work I did
was to separate the code for DX50 (&DX90) from the android target.
On this Target Ilia used source from tinyalsa from AOSP. I did not touch that part of the code because I do not understand it.
What else I changed from Ilias sources besides the separation from the target "android":
* removed a dirty hack to keep backlight off
* changed value battery meter to voltage battery meter
* made all plugins compile (named target as "standalone") and added keymaps
* i added the graphics for the manual but did not do anything else for the manual yet
* minor optimizations

known bugs:
* timers are slowed donw when playback is active (tinyalsa related?)
* some minor bugs

Things to do:
* The main prolem will be how to install the app correctly. A guy called DOC2008 added a CWM (by androtab.info) to the
  official firmware and Ilia made a CWM installation script and a dualboot selector (rbutils/ibassoboot, build with
  ndk-build). We will have to find a way to install rockbox in a proper way without breaking any copyrights.
  Maybe ADB is an option but it is not enable with OF by default. Patching the OF is probably the way to go.
* All the wiki and manual

to build:
needed: android ndk installed, android sdk installed with additional build-tools 19.1.0 installed

./tools/configure
select iBasso DX50 or iBasso DX90
make -j apk

the content of rockbox.zip/.rockbox needs to be copied to /system/rockbox/app_rockbox/rockbox/ (rockbox app not needed)
the content of libs/armeabi to /system/rockbox/lib/ (rockbox app needed)

The boot selector is needed as /system/bin/MangoPlayer and the iBasso app as /system/bin/MangoPlayer_original. There
is also the "vold" file. The one from OF does not work with DX50 rockbox (DX90 works!?), the one from Ilia is necessary.

Until we have found a proper way to install it, it can only be installed following the instructions of Ilia on his
bitbucket page, using the CWM-OF and his installation script package.

Change-Id: Ic4faaf84824c162aabcc08e492cee6e0068719d0
Reviewed-on: http://gerrit.rockbox.org/941
Tested: Chiwen Chang <rock1104.tw@yahoo.com.tw>
Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
2014-09-18 18:19:01 +02:00

1046 lines
32 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2004-2006 Antoine Cellerier <dionoea -at- videolan -dot- org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "lib/playback_control.h"
/* what the minesweeper() function can return */
enum minesweeper_status {
MINESWEEPER_WIN,
MINESWEEPER_LOSE,
MINESWEEPER_QUIT,
MINESWEEPER_USB
};
/* variable button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_OFF
# define MINESWP_TOGGLE BUTTON_ON
# define MINESWP_TOGGLE2 BUTTON_F1
# define MINESWP_DISCOVER BUTTON_PLAY
# define MINESWP_DISCOVER2 BUTTON_F2
# define MINESWP_INFO BUTTON_F3
#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_OFF
# define MINESWP_TOGGLE BUTTON_ON
# define MINESWP_TOGGLE2 BUTTON_F1
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_DISCOVER2 BUTTON_F2
# define MINESWP_INFO BUTTON_F3
#elif CONFIG_KEYPAD == ONDIO_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_OFF
# define MINESWP_TOGGLE_PRE BUTTON_MENU
# define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
# define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
# define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_OFF
# define MINESWP_TOGGLE BUTTON_ON
# define MINESWP_TOGGLE2 BUTTON_REC
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_MODE
# define MINESWP_RC_QUIT BUTTON_RC_STOP
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
# define MINESWP_SCROLLWHEEL
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_MENU
# define MINESWP_DOWN BUTTON_PLAY
# define MINESWP_NEXT BUTTON_SCROLL_FWD
# define MINESWP_PREV BUTTON_SCROLL_BACK
# define MINESWP_QUIT (BUTTON_SELECT | BUTTON_MENU)
# define MINESWP_TOGGLE_PRE BUTTON_SELECT
# define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
# define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
# define MINESWP_INFO (BUTTON_SELECT | BUTTON_PLAY)
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_REC
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_A
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_MENU
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
# define MINESWP_SCROLLWHEEL
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_NEXT BUTTON_SCROLL_FWD
# define MINESWP_PREV BUTTON_SCROLL_BACK
# define MINESWP_TOGGLE BUTTON_REC
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO (BUTTON_REC|BUTTON_REPEAT)
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT (BUTTON_HOME|BUTTON_REPEAT)
# define MINESWP_TOGGLE BUTTON_SCROLL_FWD
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_SCROLL_BACK
#elif (CONFIG_KEYPAD == SANSA_C200_PAD) || \
(CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
(CONFIG_KEYPAD == SANSA_M200_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE_PRE BUTTON_SELECT
# define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
# define MINESWP_TOGGLE2 BUTTON_VOL_DOWN
# define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
# define MINESWP_DISCOVER2 BUTTON_VOL_UP
# define MINESWP_INFO (BUTTON_SELECT | BUTTON_UP)
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_SCROLL_UP
# define MINESWP_DOWN BUTTON_SCROLL_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_REW
# define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_BACK
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_MENU
#elif (CONFIG_KEYPAD == MROBE100_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_DISPLAY
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_MENU
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
# define MINESWP_LEFT BUTTON_RC_REW
# define MINESWP_RIGHT BUTTON_RC_FF
# define MINESWP_UP BUTTON_RC_VOL_UP
# define MINESWP_DOWN BUTTON_RC_VOL_DOWN
# define MINESWP_QUIT BUTTON_RC_REC
# define MINESWP_TOGGLE BUTTON_RC_MODE
# define MINESWP_DISCOVER BUTTON_RC_PLAY
# define MINESWP_INFO BUTTON_RC_MENU
#elif (CONFIG_KEYPAD == COWON_D2_PAD)
# define MINESWP_QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_BACK
# define MINESWP_TOGGLE BUTTON_SELECT
# define MINESWP_DISCOVER BUTTON_PLAY
# define MINESWP_INFO BUTTON_MENU
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_VIEW
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_MENU
#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_NEXT
# define MINESWP_DISCOVER BUTTON_PLAY
# define MINESWP_INFO BUTTON_MENU
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
# define MINESWP_LEFT BUTTON_PREV
# define MINESWP_RIGHT BUTTON_NEXT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_RIGHT
# define MINESWP_DISCOVER BUTTON_PLAY
# define MINESWP_INFO BUTTON_MENU
#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
CONFIG_KEYPAD == ONDAVX777_PAD || \
CONFIG_KEYPAD == MROBE500_PAD
# define MINESWP_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YH920_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_REC
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_REW
# define MINESWP_INFO BUTTON_FFWD
#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
# define MINESWP_LEFT BUTTON_PREV
# define MINESWP_RIGHT BUTTON_NEXT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_REC
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_OK
# define MINESWP_INFO BUTTON_MENU
#elif CONFIG_KEYPAD == MPIO_HD200_PAD
# define MINESWP_LEFT BUTTON_VOL_DOWN
# define MINESWP_RIGHT BUTTON_VOL_UP
# define MINESWP_UP BUTTON_REW
# define MINESWP_DOWN BUTTON_FF
# define MINESWP_QUIT (BUTTON_REC|BUTTON_PLAY)
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_FUNC
# define MINESWP_INFO BUTTON_REC
#elif CONFIG_KEYPAD == MPIO_HD300_PAD
# define MINESWP_LEFT BUTTON_REW
# define MINESWP_RIGHT BUTTON_FF
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT (BUTTON_MENU|BUTTON_REPEAT)
# define MINESWP_TOGGLE BUTTON_ENTER
# define MINESWP_DISCOVER BUTTON_PLAY
# define MINESWP_INFO (BUTTON_MENU|BUTTON_REL)
#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_SELECT
# define MINESWP_DISCOVER BUTTON_PLAYPAUSE
# define MINESWP_NEXT BUTTON_BOTTOMRIGHT
# define MINESWP_PREV BUTTON_BOTTOMLEFT
# define MINESWP_INFO BUTTON_BACK
#elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
# define MINESWP_SCROLLWHEEL
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_NEXT BUTTON_SCROLL_FWD
# define MINESWP_PREV BUTTON_SCROLL_BACK
# define MINESWP_TOGGLE BUTTON_NEXT
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_PREV
#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_BACK
# define MINESWP_TOGGLE BUTTON_USER
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_MENU
#elif (CONFIG_KEYPAD == HM60X_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE (BUTTON_LEFT|BUTTON_POWER)
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO (BUTTON_UP|BUTTON_POWER)
#elif (CONFIG_KEYPAD == HM801_PAD)
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_PLAY
# define MINESWP_DISCOVER BUTTON_SELECT
# define MINESWP_INFO BUTTON_PREV
#elif (CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD)
# define MINESWP_LEFT BUTTON_BACK
# define MINESWP_RIGHT BUTTON_MENU
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE BUTTON_VOL_UP
# define MINESWP_DISCOVER BUTTON_PLAY
# define MINESWP_INFO BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == SONY_NWZ_PAD
#define MINESWP_LEFT BUTTON_LEFT
#define MINESWP_RIGHT BUTTON_RIGHT
#define MINESWP_UP BUTTON_UP
#define MINESWP_DOWN BUTTON_DOWN
#define MINESWP_QUIT BUTTON_BACK
#define MINESWP_TOGGLE BUTTON_PLAY
#define MINESWP_DISCOVER (BUTTON_PLAY|BUTTON_REPEAT)
#define MINESWP_INFO BUTTON_POWER
#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
#define MINESWP_LEFT BUTTON_LEFT
#define MINESWP_RIGHT BUTTON_RIGHT
#define MINESWP_UP BUTTON_UP
#define MINESWP_DOWN BUTTON_DOWN
#define MINESWP_QUIT BUTTON_BACK
#define MINESWP_TOGGLE BUTTON_PLAYPAUSE
#define MINESWP_DISCOVER BUTTON_SELECT
#define MINESWP_INFO BUTTON_MENU
#elif (CONFIG_KEYPAD == DX50_PAD)
# define MINESWP_QUIT (BUTTON_POWER|BUTTON_REL)
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef MINESWP_QUIT
# define MINESWP_QUIT BUTTON_TOPLEFT
#endif
#ifndef MINESWP_LEFT
# define MINESWP_LEFT BUTTON_MIDLEFT
#endif
#ifndef MINESWP_RIGHT
# define MINESWP_RIGHT BUTTON_MIDRIGHT
#endif
#ifndef MINESWP_UP
# define MINESWP_UP BUTTON_TOPMIDDLE
#endif
#ifndef MINESWP_DOWN
# define MINESWP_DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef MINESWP_TOGGLE
# define MINESWP_TOGGLE BUTTON_CENTER
#endif
#ifndef MINESWP_DISCOVER
# define MINESWP_DISCOVER BUTTON_BOTTOMLEFT
#endif
#ifndef MINESWP_INFO
# define MINESWP_INFO BUTTON_BOTTOMRIGHT
#endif
#endif
extern const fb_data minesweeper_tiles[];
#ifdef HAVE_LCD_COLOR
# if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
/* We want to have at least 130 tiles on the screen */
# define TileSize 16
# elif ( LCD_HEIGHT * LCD_WIDTH ) / ( 12 * 12 ) >= 130
# define TileSize 12
# else
# define TileSize 10
# endif
# define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
#elif LCD_DEPTH > 1
# define TileSize 12
#else
# define TileSize 8
#endif
#define Mine 9
#define Flag 10
#define Unknown 11
#define ExplodedMine 12
#define WrongFlag 13
#define CorrectFlag 14
#define draw_tile( num, x, y ) \
rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
TileSize, left+x*TileSize, top+y*TileSize, \
TileSize, TileSize )
#define invert_tile( x, y ) \
rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
rb->lcd_set_drawmode(DRMODE_SOLID);
/* the tile struct
* if there is a mine, mine is true
* if tile is known by player, known is true
* if tile has a flag, flag is true
* neighbors is the total number of mines arround tile
*/
typedef struct tile
{
unsigned char mine : 1;
unsigned char known : 1;
unsigned char flag : 1;
unsigned char neighbors : 4;
} tile;
/* the height and width of the field */
#define MAX_HEIGHT (LCD_HEIGHT/TileSize)
#define MAX_WIDTH (LCD_WIDTH/TileSize)
static int height = MAX_HEIGHT;
static int width = MAX_WIDTH;
static int top;
static int left;
/* The Minefield. Caution it is defined as Y, X! Not the opposite. */
static tile minefield[MAX_HEIGHT][MAX_WIDTH];
/* total number of mines on the game */
static int mine_num = 0;
/* percentage of mines on minefield used during generation */
static int percent = 16;
/* number of tiles left on the game */
static int tiles_left;
/* Because mines are set after the first move... */
static bool no_mines = true;
/* We need a stack (created on discover()) for the cascade algorithm. */
static int stack_pos = 0;
#ifdef HAVE_TOUCHSCREEN
#include "lib/pluginlib_touchscreen.h"
static struct ts_raster mine_raster = { 0, 0, MAX_WIDTH, MAX_HEIGHT, TileSize, TileSize };
#endif
static void push( int *stack, int y, int x )
{
if( stack_pos <= height*width )
{
stack[++stack_pos] = y;
stack[++stack_pos] = x;
}
}
/* Unveil tiles and push them to stack if they are empty. */
static void unveil( int *stack, int y, int x )
{
if( x < 0 || y < 0 || x > width - 1 || y > height - 1
|| minefield[y][x].known
|| minefield[y][x].mine || minefield[y][x].flag ) return;
minefield[y][x].known = 1;
if( minefield[y][x].neighbors == 0 )
push( stack, y, x );
}
static int is_flagged( int y, int x )
{
if( x >= 0 && y >= 0 && x < width && y < height && minefield[y][x].flag )
return 1;
return 0;
}
static int neighbors_flagged( int y, int x )
{
return is_flagged( y-1, x-1 ) +
is_flagged( y-1, x ) +
is_flagged( y-1, x+1 ) +
is_flagged( y, x-1 ) +
is_flagged( y, x ) +
is_flagged( y, x+1 ) +
is_flagged( y+1, x-1 ) +
is_flagged( y+1, x ) +
is_flagged( y+1, x+1 );
}
static bool discover( int y, int x, bool explore_neighbors )
{
/* Selected tile. */
if( x < 0 || y < 0 || x > width - 1 || y > height - 1)
return false;
if( minefield[y][x].known || minefield[y][x].mine || minefield[y][x].flag )
{
if( !minefield[y][x].flag && minefield[y][x].mine )
return true;
if( explore_neighbors && minefield[y][x].known &&
minefield[y][x].neighbors == neighbors_flagged( y, x ) )
{
return discover( y-1, x-1, false ) ||
discover( y-1, x, false ) ||
discover( y-1, x+1, false ) ||
discover( y, x-1, false ) ||
discover( y, x, false ) ||
discover( y, x+1, false ) ||
discover( y+1, x-1, false ) ||
discover( y+1, x, false ) ||
discover( y+1, x+1, false );
}
return false;
}
minefield[y][x].known = 1;
/* Exit if the tile is not empty. (no mines nearby) */
if( minefield[y][x].neighbors ) return false;
int stack[height*width];
push( stack, y, x );
/* Scan all nearby tiles. If we meet a tile with a number we just unveil
* it. If we meet an empty tile, we push the location in stack. For each
* location in stack we do the same thing. (scan again all nearby tiles)
*/
while( stack_pos )
{
/* Pop x, y from stack. */
x = stack[stack_pos--];
y = stack[stack_pos--];
unveil( stack, y-1, x-1 );
unveil( stack, y-1, x );
unveil( stack, y-1, x+1 );
unveil( stack, y, x+1 );
unveil( stack, y+1, x+1 );
unveil( stack, y+1, x );
unveil( stack, y+1, x-1 );
unveil( stack, y, x-1 );
}
return false;
}
/* Reset the whole board for a new game. */
static void minesweeper_init( void )
{
rb->memset(minefield, 0, sizeof(minefield));
no_mines = true;
tiles_left = width*height;
}
/* put mines on the mine field */
/* there is p% chance that a tile is a mine */
/* if the tile has coordinates (x,y), or is adjacent to those,
* then it can't be a mine because that would reduce the game
* from a logic game to a guessing game. */
static void minesweeper_putmines( int p, int x, int y )
{
int i,j;
mine_num = 0;
for( i = 0; i < height; i++ )
{
for( j = 0; j < width; j++ )
{
if( rb->rand()%100 < p
&& !( i>=y-1 && i<=y+1 && j>=x-1 && j<=x+1 ) )
{
minefield[i][j].mine = 1;
mine_num++;
}
else
{
minefield[i][j].mine = 0;
}
minefield[i][j].neighbors = 0;
}
}
/* we need to compute the neighbor element for each tile */
for( i = 0; i < height; i++ )
{
for( j = 0; j < width; j++ )
{
if( i > 0 )
{
if( j > 0 )
minefield[i][j].neighbors += minefield[i-1][j-1].mine;
minefield[i][j].neighbors += minefield[i-1][j].mine;
if( j < width - 1 )
minefield[i][j].neighbors += minefield[i-1][j+1].mine;
}
if( j > 0 )
minefield[i][j].neighbors += minefield[i][j-1].mine;
if( j < width - 1 )
minefield[i][j].neighbors += minefield[i][j+1].mine;
if( i < height - 1 )
{
if( j > 0 )
minefield[i][j].neighbors += minefield[i+1][j-1].mine;
minefield[i][j].neighbors += minefield[i+1][j].mine;
if( j < width - 1 )
minefield[i][j].neighbors += minefield[i+1][j+1].mine;
}
}
}
no_mines = false;
/* In case the user is lucky and there are no mines positioned. */
if( !mine_num && height*width != 1 )
{
minesweeper_putmines(p, x, y);
}
}
/* A function that will uncover all the board, when the user wins or loses.
can easily be expanded, (just a call assigned to a button) as a solver. */
static void mine_show( void )
{
int i, j, button;
for( i = 0; i < height; i++ )
{
for( j = 0; j < width; j++ )
{
if( minefield[i][j].mine )
{
if( minefield[i][j].known )
draw_tile( ExplodedMine, j, i );
else if( minefield[i][j].flag )
draw_tile( CorrectFlag, j, i );
else
draw_tile( Mine, j, i );
}
else
{
if( minefield[i][j].flag )
draw_tile( WrongFlag, j, i );
else
draw_tile( minefield[i][j].neighbors, j, i );
}
}
}
rb->lcd_update();
do
button = rb->button_get(true);
while( ( button == BUTTON_NONE )
|| ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
#ifdef HAVE_TOUCHSCREEN
button = BUTTON_NONE;
#endif
}
static int count_tiles_left( void )
{
int tiles = 0;
int i, j;
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
if( minefield[i][j].known == 0 )
tiles++;
return tiles;
}
static int count_flags( void )
{
int flags = 0;
int i, j;
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
if( minefield[i][j].flag == 1 )
flags++;
return flags;
}
/* welcome screen where player can chose mine percentage */
static enum minesweeper_status menu( void )
{
int selection = 0, result = MINESWEEPER_QUIT;
bool menu_quit = false;
MENUITEM_STRINGLIST( menu, "Minesweeper Menu", NULL, "Play Minesweeper",
"Mine Percentage", "Number of Rows",
"Number of Columns", "Playback Control", "Quit" );
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground( rb->global_settings->fg_color );
rb->lcd_set_background( rb->global_settings->bg_color );
#endif
while( !menu_quit )
{
switch( rb->do_menu( &menu, &selection, NULL, false ) )
{
case 0:
result = MINESWEEPER_WIN; /* start playing */
menu_quit = true;
break;
case 1:
rb->set_int( "Mine Percentage", "%", UNIT_INT, &percent, NULL,
1, 2, 98, NULL );
break;
case 2:
rb->set_int( "Number of Rows", "", UNIT_INT, &height, NULL,
1, 1, MAX_HEIGHT, NULL );
break;
case 3:
rb->set_int( "Number of Columns", "", UNIT_INT, &width, NULL,
1, 1, MAX_WIDTH, NULL );
break;
case 4:
playback_control( NULL );
break;
default:
result = MINESWEEPER_QUIT; /* quit program */
menu_quit = true;
break;
}
}
return result;
}
/* the big and ugly game function */
static enum minesweeper_status minesweeper( void )
{
int i, j;
int button;
#if defined(HAVE_TOUCHSCREEN) || defined(MINESWP_TOGGLE_PRE)
int lastbutton = BUTTON_NONE;
#endif
/* the cursor coordinates */
int x=0, y=0;
/**
* Show the menu
*/
if( ( i = menu() ) != MINESWEEPER_WIN ) return i;
/**
* Init game
*/
top = (LCD_HEIGHT-height*TileSize)/2;
left = (LCD_WIDTH-width*TileSize)/2;
#ifdef HAVE_TOUCHSCREEN
mine_raster.tl_x = left;
mine_raster.tl_y = top;
mine_raster.width = width*TileSize;
mine_raster.height = height*TileSize;
#endif
rb->srand( *rb->current_tick );
minesweeper_init();
x = 0;
y = 0;
/**
* Play
*/
while( true )
{
/* clear the screen buffer */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_background( BackgroundColor );
#endif
rb->lcd_clear_display();
/* display the mine field */
for( i = 0; i < height; i++ )
{
for( j = 0; j < width; j++ )
{
if( minefield[i][j].known )
{
draw_tile( minefield[i][j].neighbors, j, i );
}
else if(minefield[i][j].flag)
{
draw_tile( Flag, j, i );
}
else
{
draw_tile( Unknown, j, i );
}
}
}
/* display the cursor */
invert_tile( x, y );
/* update the screen */
rb->lcd_update();
button = rb->button_get(true);
#ifdef HAVE_TOUCHSCREEN
if(button & BUTTON_TOUCHSCREEN)
{
struct ts_raster_result res;
if(touchscreen_map_raster(&mine_raster, rb->button_get_data() >> 16, rb->button_get_data() & 0xffff, &res) == 1)
{
button &= ~BUTTON_TOUCHSCREEN;
lastbutton &= ~BUTTON_TOUCHSCREEN;
if(button & BUTTON_REPEAT && lastbutton != MINESWP_TOGGLE && lastbutton ^ BUTTON_REPEAT)
button = MINESWP_TOGGLE;
else if(button == BUTTON_REL && lastbutton ^ BUTTON_REPEAT)
button = MINESWP_DISCOVER;
else
button |= BUTTON_TOUCHSCREEN;
x = res.x;
y = res.y;
}
}
#endif
switch(button)
{
/* quit minesweeper (you really shouldn't use this button ...) */
#ifdef MINESWP_RC_QUIT
case MINESWP_RC_QUIT:
#endif
case MINESWP_QUIT:
return MINESWEEPER_QUIT;
/* move cursor left */
case MINESWP_LEFT:
case MINESWP_LEFT|BUTTON_REPEAT:
if( --x < 0)
x += width;
break;
/* move cursor right */
case MINESWP_RIGHT:
case MINESWP_RIGHT|BUTTON_REPEAT:
if( ++x >= width )
x -= width;
break;
/* move cursor down */
case MINESWP_DOWN:
case MINESWP_DOWN|BUTTON_REPEAT:
if( ++y >= height )
y -= height;
break;
/* move cursor up */
case MINESWP_UP:
case MINESWP_UP|BUTTON_REPEAT:
if( --y < 0 )
y += height;
break;
/*move cursor though the entire field*/
#ifdef MINESWP_SCROLLWHEEL
case MINESWP_NEXT:
case MINESWP_NEXT|BUTTON_REPEAT:
if (x == width -1 ) {
if( ++y >= height )
y -= height;
}
if( ++x >= width )
x -= width;
break;
case MINESWP_PREV:
case MINESWP_PREV|BUTTON_REPEAT:
if (x == 0) {
if( --y < 0 )
y += height;
}
if( --x < 0 )
x += width;
break;
#endif
/* discover a tile (and it's neighbors if .neighbors == 0) */
case MINESWP_DISCOVER:
#ifdef MINESWP_DISCOVER2
case MINESWP_DISCOVER2:
#endif
if( minefield[y][x].flag ) break;
/* we put the mines on the first "click" so that you don't
* lose on the first "click" */
if( tiles_left == width*height && no_mines )
minesweeper_putmines(percent,x,y);
if( discover( y, x, true ) )
{
minefield[y][x].known = 1;
return MINESWEEPER_LOSE;
}
tiles_left = count_tiles_left();
if( tiles_left == mine_num )
{
return MINESWEEPER_WIN;
}
break;
/* toggle flag under cursor */
case MINESWP_TOGGLE:
#ifdef MINESWP_TOGGLE_PRE
if( lastbutton != MINESWP_TOGGLE_PRE )
break;
#endif
#ifdef MINESWP_TOGGLE2
case MINESWP_TOGGLE2:
#endif
if( !minefield[y][x].known )
minefield[y][x].flag = !minefield[y][x].flag;
break;
/* show how many mines you think you have found and how many
* there really are on the game */
case MINESWP_INFO:
if( no_mines )
break;
int flags_used = count_flags();
if (flags_used == 1) {
rb->splashf( HZ*2, "You marked 1 field. There are %d mines.",
mine_num );
}
else
{
rb->splashf( HZ*2, "You marked %d fields. There are %d mines.",
flags_used, mine_num );
}
break;
default:
if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
return MINESWEEPER_USB;
break;
}
#if defined(HAVE_TOUCHSCREEN) || defined(MINESWP_TOGGLE_PRE)
if( button != BUTTON_NONE )
lastbutton = button;
#endif
}
}
/* plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
bool exit = false;
(void)parameter;
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
while( !exit )
{
switch( minesweeper() )
{
case MINESWEEPER_WIN:
rb->splash( HZ, "You Win!" );
rb->lcd_clear_display();
mine_show();
break;
case MINESWEEPER_LOSE:
rb->splash( HZ, "You Lose!" );
rb->lcd_clear_display();
mine_show();
break;
case MINESWEEPER_USB:
return PLUGIN_USB_CONNECTED;
case MINESWEEPER_QUIT:
exit = true;
break;
default:
break;
}
}
return PLUGIN_OK;
}