作者:雷神QQ:38929568
QQ群:28048051(满)28047782(将满)

新建一个j2me midlet工程
在src里添加如下连个类
Minesweeper.java    
cGame.java

详尽注释,方便初学者!

工程文件已经打包上传到csdn,地址如下
download.csdn.net/user/kome2000/

//////////////////////////////////////////////////////////////////////////////////////////
//
// Minesweeper.java
//
// Project: Minesweeper
// Author(s): Gao Lei
// Create: 2007-10-08
/////////////////////////////////////////////////////////////////////////////////////////
/
import javax.microedition.midlet.*;    //j2me MIDlet程序必须继承MIDlet类,所以要引入此包
import javax.microedition.lcdui.*;    //Display这个类所在包

///////////////////////////////////////////////////////////////////////////////////////////

public class Minesweeper extends MIDlet 
{
    
static Minesweeper s_midlet;    //MIDlet类的静态对象,方便实用 MIDlet类方法
    static Display s_display = null;//用来显示 Canvas
    static cGame s_game = null;        //Canvas类对象,主要实现游戏的类
    
    
public Minesweeper()
    
{
        s_midlet 
= this;
    }

    
    
/**
     * 程序开始 系统会调用这个函数
     * 也有些手机 可以把程序初始化部分放到构造函数里,这连个地方应视手机的不同而定!
     
*/

    
public void startApp()             
    
{
        
if (s_display == null
        
{
            s_display 
= Display.getDisplay(this);//创建Display对象,参数是MIDlet类对象,也就是我们当前写的这个Minesweeper类
        }


        
if (s_game == null
        
{
            s_game 
= new cGame();                //创建 Canvas对象
            s_display.setCurrent(s_game);        //把Canvas对象设置成当前显示
        }
 
        
else 
        
{
            s_display.setCurrent(s_game);
        }

    }


    
/**
     * 程序暂停 系统会自动调用这个函数,不是所有手机都支持,
     * 手机在接到中断,如 来电,来短信时候会调用这个函数,这个函数 通常是空的!
     
*/

    
public void pauseApp()         
    
{
        
    }


    
/**
     * 程序关闭 系统会调用这个函数,如果希望关闭程序的时候保存数据,可在这个函数里添加保存数据的方法
     * 比如游戏进行中,按了关机键,程序就会调用这个函数,也可以在程序中调用这个函数来结束游戏!
     
*/

    
public void destroyApp(boolean unconditional) 
    
{
        notifyDestroyed();
    }

}

 cGame.java

代码如下

////////////////////////////////////////////////////////////////////////////////
//
// cGame.java
//
// Project: Minesweeper
// Author(s): Gao Lei
// Create: 2007-10-08
////////////////////////////////////////////////////////////////////////////////
import java.util.Random;            //得到 随机函数
import javax.microedition.lcdui.*;    //写界面所需要的包

////////////////////////////////////////////////////////////////////////////////

class cGame extends Canvas 
{
    
public static int      s_width = 0;    //屏幕尺寸 宽
    public static int      s_height= 0;    //屏幕尺寸 高
    public static Random rand;            //随机数对象
    private int UP         = 1;            //定义键值 上
    private int DOWN     = 2;            //定义键值 下
    private int LEFT     = 3;            //定义键值 左
    private int RIGHT     = 4;            //定义键值 右
    private int FILE     = 5;            //定义键值 中间确认键
    private int map_x     = 10;    // 15    //雷区的 行数
    private int map_y     = 10;    // 12    //雷区的 列数
    private int map_w     = 20;            //一个雷区的格子的宽度
    private int map_h     = 20;            //一个雷区的格子的高度
    private int key_x     = map_x / 2;    //游戏初始时 光标所在雷区的格子位置
    private int key_y     = map_y / 2;    //游戏初始时 光标所在雷区的格子位置
    private int mine_num= 10;            //雷区的雷数 不应该大于雷区的格子总数
    private int[][] map;                //雷区的地图数组 >=10 为雷, <10 为周围的雷数, 0位附近没有雷
    private boolean[][] map_show;        //雷区的地图数组 是否显示 该位置的雷数
    private boolean isGameOver = false;    //游戏是否结束
    
    cGame()
    
{
        setFullScreenMode(
true);        //设置游戏为全屏幕模式,该函数只能在支持midp2.0的手机上使用
        s_width = getWidth();            //得到屏幕尺寸 宽
        s_height= getHeight();            //得到屏幕尺寸  高
        rePlay();                        //游戏初始化//重新游戏
    }

    
    
/**
     * 系统自动调用该绘图函数,并传入绘图设备g,通过该设备,我们可以绘制如直线,矩形快,字符串,图片等,
     
*/

    
public void paint(Graphics g)
    
{
        g.setClip(
00, s_width, s_height);        //设置参数描述的区域为操作区
        g.setColor(0x000000);                    //设置颜色为 黑色, 三个16进制数表示,RGB,如0x00ff00 为绿色
        g.fillRect(00, s_width, s_height);    //绘制一个实心矩形区域
        g.setColor(0xFFFFFF);                    //设置颜色为 白色
        
//绘制雷区
        forint i=0; i<=map_y; i++ )    // |||    //画 map_y+1条竖线
        {
            g.drawLine(i
*map_w, 0, i*map_w, map_h*map_x);
        }

        
forint i=0; i<=map_x; i++ )     // ---    //画 map_x+1条横线
        {
            g.drawLine(
0, i*map_h, map_y*map_w, i*map_h);
        }


        g.setColor(
0xFF0000);                    //设置颜色 红
        g.drawRect(key_x*map_w+1, key_y*map_h+1, map_w-2, map_h-2);    //绘制一个空心矩形框
        
        
forint i=0; i<map_x; i++ )            
        
{
            
forint j=0; j<map_y; j++ )
            
{
                
if( map_show[i][j] )            //遍历地图数组 看该位置的雷数 是否应该显示
                    g.drawString(""+map[i][j], j*map_h+5, i*map_w+50);    //显示该位置的雷数
            }
    
        }

        
if( isGameOver )                        //如果游戏 结束
            g.drawString("GAME OVER", s_width/2, s_height, g.HCENTER|g.BOTTOM);    //显示 GAME OVER
    }


    
/**
     * 系统自动调用该函数,当有键盘事件发生为按下某键,参数key为按下键的键值
     
*/

    
public void keyPressed(int key)
    
{
        key 
= Math.abs(key);
        System.out.println(
"key="+key);
        
//上下左右 为移动光标事件,只需要调整光标位置即可,但需要做边界判断
        if( key == this.UP )
        
{
            key_y
--;
            
if( key_y<0 )    
                key_y 
= 0;
        }

        
else if( key == this.DOWN )
        
{
            key_y
++;
            
if( key_y>=map_x-1 )
                key_y 
=map_x-1;
        }

        
else if( key == this.LEFT )
        
{
            key_x
--;
            
if( key_x<0 )    
                key_x 
= 0;
        }

        
else if( key == this.RIGHT )
        
{
            key_x
++;
            
if( key_x>=map_y-1 )
                key_x 
=map_y-1;
        }

        
else if( key==FILE || key==KEY_NUM5)//按下的为确定键
        {
            
if!isGameOver )                //如果游戏没结束    //结束了就不做确认键操作了    
            {
                showMap( key_y, key_x );    
//显示该位置的雷数
                if( map[key_y][key_x] >=10 )//如果雷数>=10 该位置是雷,
                    isGameOver = true;        //游戏结束
            }

        }

        
else if( key == this.KEY_NUM0 )        //当按下 数字键 0
        {
            rePlay();                        
//重新开始游戏
        }

        
/**
         * 重新执行 paint() 但该函数是立刻返回,也就是说他不会等待paint()执行完毕就返回了,
         * 如果 需要 paint()执行完毕才返回,可以使用serviceRepaints(),也可以两个都是用,但
         * repaint()应该在serviceRepaints()之前.
         
*/

        
this.repaint();                        //本例为键盘事件驱动,刷新函数也就是每次按下键盘才作
    }


    
//该函数是一个递归函数,把当前位置设置成显示,并判断当前位置雷数是否为0个
    
//如果是0个雷,那么它周围的8个格子都要再作一次showMap
    void showMap(int x, int y)
    
{
        
if( map_show[x][y] )
            
return;
        
else
            map_show[x][y] 
= true;
        
if( map[x][y] == 0 )
        
{
            
if( x-1 >= 0 )
            
{
                showMap( x
-1, y );
                
if( y-1 >= 0)        showMap( x-1, y-1 );
                
if( y+1 < map_y)    showMap( x-1, y+1 );
            }

            
if( y-1 >= 0)            showMap( x  , y-1 );
            
if( y+1 < map_y)        showMap( x  , y+1 );
            
if( x+1 < map_x )
            
{
                showMap( x
+1, y );
                
if( y-1 >= 0)        showMap( x+1, y-1 );
                
if( y+1 < map_y)    showMap( x+1, y+1 );
            }

        }

        
    }

    
    
//重新 开始 游戏
    public void rePlay()
    
{
        isGameOver    
= false;
        map          
= new int        [map_x][map_y];
        map_show     
= new boolean    [map_x][map_y];
        rand        
= new Random( System.currentTimeMillis() );    //用事件作随机数种子的随机数
        
//布雷
        for(int i=0; i<mine_num; i++)    //随机mine_num个雷的位置
        {
            
int x = rand.nextInt( map_x );    //得到 随机数 雷格子的x方向位置
            int y = rand.nextInt( map_y );    //得到 随机数 雷格子的y方向位置
            if( map[x][y] >= 10)            //如果该位置已经是雷了,就要重新布雷
            {    
                i
--;
                
continue;
            }

            map[x][y] 
= 10;                    //否则 将该位置 设定为雷
            
//并在该雷的周围 的雷数都作+1操作
            
//以下判断为 边角判断,防止访问数组越界
            if( x-1 >= 0 )                    
            
{
                map[x
-1][y  ] += 1;
                
if( y-1 >= 0)        map[x-1][y-1+= 1;
                
if( y+1 < map_y)    map[x-1][y+1+= 1;
            }

            
if( y-1 >= 0)        map[x  ][y-1+= 1;
            
if( y+1 < map_y)    map[x  ][y+1+= 1;
            
if( x+1 < map_x )
            
{
                map[x
+1][y  ] += 1;
                
if( y-1 >= 0)        map[x+1][y-1+= 1;
                
if( y+1 < map_y)    map[x+1][y+1+= 1;
            }

        }

    }

}


这样200行的小游戏就做好了!可以玩了, 不过,这个还没有完成,没有标志是雷,和全部找到雷的成功完成游戏界面,只是基本的布雷,挖雷,在下一篇,我们将完善这个小游戏!

扫雷(2)是在扫雷(1)的基础上增加 完善了部分代码基本逻辑不变!
使得游戏更好玩了,代码两也增加到400行,比较适合初学者,可读性强,有详尽的代码注释。
数字键1标红旗,不确定,取消标记。数字键3显示/不显示 游戏信息!
方向键,数字键2468,控制光标上下左右移动!

工程文件已经打包上传到csdn,地址如下
download.csdn.net/user/kome2000/

Minesweeper.java
这个类是游戏入口类,基本不做修改!而且每个游戏的这个midlet类都差不太多! 

/////////////////////////////////////////////////////////////////////////////////////////
//
// Minesweeper.java
//
// Project: Minesweeper
// Author(s): Gao Lei
// Create: 2007-10-08
/////////////////////////////////////////////////////////////////////////////////////////
/
import javax.microedition.midlet.*;    //j2me MIDlet程序必须继承MIDlet类,所以要引入此包
import javax.microedition.lcdui.*;    //Display这个类所在包

//////////////////////////////////////////////////////////////////////////////////////////

public class Minesweeper extends MIDlet 
{
    
static Minesweeper s_midlet;    //MIDlet类的静态对象,方便实用 MIDlet类方法
    static Display s_display = null;//用来显示 Canvas
    static cGame s_game = null;        //Canvas类对象,主要实现游戏的类
    
    
public Minesweeper()
    
{
        s_midlet 
= this;
    }

    
    
/**
     * 程序开始 系统会调用这个函数
     * 也有些手机 可以把程序初始化部分放到构造函数里,这连个地方应视手机的不同而定!
     
*/

    
public void startApp()             
    
{
        
if (s_display == null
        
{
            s_display 
= Display.getDisplay(this);//创建Display对象,参数是MIDlet类对象,也就是我们当前写的这个Minesweeper类
        }


        
if (s_game == null
        
{
            s_game 
= new cGame();                //创建 Canvas对象
            s_display.setCurrent(s_game);        //把Canvas对象设置成当前显示
        }
 
        
else 
        
{
            s_display.setCurrent(s_game);
        }

    }


    
/**
     * 程序暂停 系统会自动调用这个函数,不是所有手机都支持,
     * 手机在接到中断,如 来电,来短信时候会调用这个函数,这个函数 通常是空的!
     
*/

    
public void pauseApp()         
    
{
        
    }


    
/**
     * 程序关闭 系统会调用这个函数,如果希望关闭程序的时候保存数据,可在这个函数里添加保存数据的方法
     * 比如游戏进行中,按了关机键,程序就会调用这个函数,也可以在程序中调用这个函数来结束游戏!
     
*/

    
public void destroyApp(boolean unconditional) 
    
{
        notifyDestroyed();
    }

}

 

cGame.java 这个类添加了几个方法,主要是绘制图形,和完善游戏,增加了游戏胜利和游戏难度。


////////////////////////////////////////////////////////////////////////////////
//
// cGame.java
//
// Project: Minesweeper
// Author(s): Gao Lei
// Create: 2007-10-08
////////////////////////////////////////////////////////////////////////////////

import java.util.Random;            //得到 随机函数
import javax.microedition.lcdui.*;    //写界面所需要的包

////////////////////////////////////////////////////////////////////////////////

class cGame extends Canvas 
{
    
//游戏状态
    private static final int STATEPLAY        = 0;    //游戏中
    private static final int STATELOST        = 1;    //游戏失败
    private static final int STATEWIN        = 2;    //游戏胜利
    
//格子状态
    private static final int MINE_OFF_SHOW    = 0;    //不显示格子中雷的数
    private static final int MINE_ON_SHOW    = 1;    //显示格子中雷的数
    private static final int MINE_ASK        = 9;    //设置问号
    private static final int MINE_FLAG        = 10;    //设置红旗
    private static final int MINE_GUESS_ERR    = 11;    //显示猜错了雷
    
//定义键值
    private static final int KEY_UP         = 1;    //
    private static final int KEY_DOWN         = 2;    //
    private static final int KEY_LEFT         = 3;    //
    private static final int KEY_RIGHT         = 4;    //
    private static final int KEY_FIRE         = 5;    //中间确认键

    
public static Random rand;            //随机数对象

    
private int map_x     = 10;            //雷区的 行数        // 15
    private int map_y     = 10;            //雷区的 列数        // 12
    private int map_w     = 20;            //一个雷区的格子的宽度
    private int map_h     = 20;            //一个雷区的格子的高度
    private int key_x     = map_x / 2;    //游戏初始时 光标所在雷区的格子位置
    private int key_y     = map_y / 2;    //游戏初始时 光标所在雷区的格子位置
    private int mine_num= 10;            //雷区的雷数 不应该大于雷区的格子总数
    private int flagNum    = mine_num;        //剩余红旗数 
    private int rightNum= 0;            //猜对的雷数
    private int[][] map;                //雷区的地图数组 >=10 为雷, <10 为周围的雷数, 0位附近没有雷
    private int[][] map_show;            //雷区的地图数组是否显示该位置的雷数//1显示//0不显示//9问号//10红旗
    private int     gameState     = STATEPLAY;    //游戏状态
    private int     s_width     = 0;            //屏幕尺寸 宽
    private int     s_height    = 0;            //屏幕尺寸 高
    private int     addMine        = 0;            //重新开始后雷数增加的个数
    private boolean isShowInfo    = false;        //是否显示游戏信息
    private String     strFlagNum     = "红旗数";
    
private String     strMineNum     = "正确率";
    
private String[] gameInfo     = {"游戏中","游戏失败 按0重新开始","游戏胜利 按0进入多雷区"};
    
private Font     font         = Font.getFont( Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE );
    
    cGame()
    
{
        setFullScreenMode(
true);        //设置游戏为全屏幕模式,该函数只能在支持midp2.0的手机上使用
        s_width = getWidth();            //得到屏幕尺寸     宽
        s_height= getHeight();            //得到屏幕尺寸     高
        rePlay( 0 );                    //游戏初始化//重新游戏
    }

    
    
/**
     * 系统自动调用该绘图函数,并传入绘图设备g,通过该设备,我们可以绘制如直线,矩形快,字符串,图片等,
     
*/

    
public void paint(Graphics g)
    
{
        g.setClip(
00, s_width, s_height);        //设置参数描述的区域为操作区
        g.setColor(0x000000);                    //设置颜色为 黑色, 三个16进制数表示,RGB,如0x00ff00 为绿色
        g.fillRect(00, s_width, s_height);    //绘制一个实心矩形区域
        g.setColor(0xFFFFFF);                    //设置颜色为 白色
        
//绘制雷区
        forint i=0; i<=map_y; i++ )    // |||    //画 map_y+1条竖线
        {
            g.drawLine(i
*map_w, 0, i*map_w, map_h*map_x);
        }

        
forint i=0; i<=map_x; i++ )     // ===    //画 map_x+1条横线
        {
            g.drawLine(
0, i*map_h, map_y*map_w, i*map_h);
        }


        
forint i=0; i<map_x; i++ )            
        
{
            
forint j=0; j<map_y; j++ )
            
{
                
if( map_show[i][j] == MINE_ON_SHOW )    //遍历地图数组 看该位置的雷数 是否应该显示
                {
                    
if( map[i][j]==0 )            //周围没有雷
                    {
                        g.setColor(
0x666666);
                        g.fillRect(j
*map_h+2, i*map_w+2, map_w-3, map_h-3);
                    }

                    
else if(map[i][j]<10)        //显示周围的雷数
                    {
                        g.setColor(
0x666666);
                        g.fillRect(j
*map_h+2, i*map_w+2, map_w-3, map_h-3);
                        g.setColor(
0x00ff00);
                        g.drawString(
""+map[i][j], j*map_h+8, i*map_w+4, g.LEFT|g.TOP);    //显示该位置的雷数
                    }

                    
else                        //踩到雷了
                    {
                        g.setColor(
0xff0000);
                        g.fillRect(j
*map_h+2, i*map_w+2, map_w-3, map_h-3);
//                        g.drawString(""+map[i][j], j*map_h+5, i*map_w+4, g.LEFT|g.TOP);    //显示该位置是雷
                    }

                }

                
else if( map_show[i][j] == MINE_FLAG )        //显示红旗
                {
                    paintFlag( g, j, i );
                }

                
else if( map_show[i][j] == MINE_ASK )        //显示问号
                {
                    paintInterrogation( g, j, i );
                }

                
else if( map_show[i][j] == MINE_GUESS_ERR )//显示猜错了
                {
                    g.setColor(
0x666666);
                    g.fillRect(j
*map_h+2, i*map_w+2, map_w-3, map_h-3);
                    paintGuessErr( g, j, i );
                }

            }
    
        }

        g.setColor(
0xFF0000);                    //设置颜色 红
        g.drawRect(key_x*map_w+1, key_y*map_h+1, map_w-2, map_h-2);        //绘制一个空心矩形框//为光标
        g.drawRect(key_x*map_w+2, key_y*map_h+2, map_w-4, map_h-4);        //绘制一个空心矩形框//为光标
        
        
if( isShowInfo || gameState != STATEPLAY )    //如果游戏 结束
        {
            g.setFont( font );
            g.drawString( strFlagNum
+":"+flagNum,     20, s_height-60, g.LEFT|g.TOP );    //显示剩余旗数
            g.drawString( strMineNum+":"+rightNum +"/"+ mine_num,    20, s_height-45, g.LEFT|g.TOP );    //显示正确率 猜对雷数/总雷数
            g.drawString( gameInfo[ gameState ],     20, s_height-30, g.LEFT|g.TOP );    //显示游戏状态
        }

    }


    
/**
     * 系统自动调用该函数,当有键盘事件发生为按下某键,参数key为按下键的键值
     
*/

    
public void keyPressed(int key)
    
{
        key 
= Math.abs(key);
        System.out.println(
"key="+key);
        
//上下左右 为移动光标事件,只需要调整光标位置即可,但需要做边界判断
        switch( key )
        
{
            
case KEY_NUM2:
            
case KEY_UP:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_y
--;
                    
if( key_y<0 )
                        key_y 
= map_x-1;
                }

            
break;

            
case KEY_NUM8:
            
case KEY_DOWN:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_y
++;
                    key_y 
%=map_x;
                }

            
break;

            
case KEY_NUM4:
            
case KEY_LEFT:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_x
--;
                    
if( key_x<0 )    
                        key_x 
= map_y-1;
                }

            
break;

            
case KEY_NUM6:
            
case KEY_RIGHT:
                
if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
else
                
{
                    key_x
++;
                    key_x 
%=map_y;
                }

            
break;

            
case KEY_FIRE:
            
case KEY_NUM5:
                
if( gameState == STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                {
                    
if( map_show[key_y][key_x] == MINE_FLAG )
                        
break;
                    showMap( key_y, key_x );    
//显示该位置的雷数
                    if( map[key_y][key_x] >=10 )//如果雷数>=10 该位置是雷,
                    {
                        isWinGame();
                        addMine        
= 0;
                        isShowInfo     
= true;
                        gameState 
= STATELOST;    //游戏失败
                    }

                }

            
break;

            
case KEY_NUM1:                        //设置红旗//问号//取消
                if( gameState != STATEPLAY )    //如果游戏没结束//结束了就不做确认键操作了    
                    break;
                
switch( map_show[key_y][key_x] )
                
{
                
case MINE_OFF_SHOW:
                    map_show[key_y][key_x] 
= MINE_FLAG;
                    flagNum
--;
                    
if( flagNum == 0 )
                    
{
                        
if( isWinGame() )
                        
{
                            addMine        
= 5;
                            isShowInfo     
= true;
                            gameState      
= STATEWIN;
                        }

                        
else
                        
{
                            addMine        
= 0;
                            isShowInfo     
= true;
                            gameState      
= STATELOST;
                        }

                    }

                
break;
                
case MINE_FLAG:
                    flagNum
++;
                    map_show[key_y][key_x] 
= MINE_ASK;
                
break;
                
case MINE_ASK:
                    map_show[key_y][key_x] 
= MINE_OFF_SHOW;
                
break;
                }

            
break;
            
case KEY_NUM3:                        //是否显示游戏信息
                isShowInfo = !isShowInfo;
            
break;
            
case KEY_NUM0:                        //当按下 数字键 0
                rePlay( addMine );                    //重新开始游戏    
            break;
        }

        
        
/**
         * 重新执行 paint() 但该函数是立刻返回,也就是说他不会等待paint()执行完毕就返回了,
         * 如果 需要 paint()执行完毕才返回,可以使用serviceRepaints(),也可以两个都是用,但
         * repaint()应该在serviceRepaints()之前.
         
*/

        
this.repaint();                        //本例为键盘事件驱动,刷新函数也就是每次按下键盘才作
    }

    
    
boolean isWinGame()
    
{
        
boolean isWin = true;
        
forint i=0; i<map_x; i++ )            
        
{
            
forint j=0; j<map_y; j++ )
            
{
                
if( map_show[i][j] == MINE_FLAG )    //显示红旗
                {
                    
if( map[i][j] < 10 )            //地雷猜错了
                    {
                        map_show[i][j] 
= MINE_GUESS_ERR;
                        isWin 
= false;
                    }

                    
else
                    
{
                        rightNum 
++;
                    }

                }

            }

        }

        
return isWin;                                //群不红旗都插对了 才能通关
    }


    
void paintFlag( Graphics g, int key_x, int key_y )
    
{
        
int x = key_x*map_h+9;
        
int y = key_y*map_w+5;
        g.setColor( 
0xFF0000 );

        g.drawLine( x  , y   , x  , y
+11 );    //    |
        g.drawLine( x-2, y+11, x+3, y+11 );    //    -
        g.drawLine( x  , y   , x+5, y+ 2 );    //    >
        g.drawLine( x+5, y+2 , x  , y+ 4 );    //    >
        x += 1;
        y 
+= 1;
        g.drawLine( x  , y   , x  , y
+11 );    //    |
        g.drawLine( x-5, y+11, x+4, y+11 );    //    -
        g.drawLine( x  , y   , x+5, y+ 2 );    //    >
        g.drawLine( x+5, y+2 , x  , y+ 4 );    //    >
    }


    
void paintInterrogation( Graphics g, int key_x, int key_y )
    
{
        
int x = key_x*map_h+8;
        
int y = key_y*map_w+5;
        
        g.setColor( 
0xFF0000 );
        g.drawString(
"?", x, y, g.LEFT|g.TOP);    //    ?
    }

    
    
void paintGuessErr( Graphics g, int key_x, int key_y )
    
{
        
int x = key_x*map_h+8;
        
int y = key_y*map_w+5;
        g.setColor( 
0xFF0000 );
        g.drawString(
"x", x, y, g.LEFT|g.TOP);    //    x
    }

    
    
//该函数是一个递归函数,把当前位置设置成显示,并判断当前位置雷数是否为0个
    
//如果是0个雷,那么它周围的8个格子都要再作一次showMap
    void showMap(int x, int y)
    
{
        
if( map_show[x][y] == MINE_FLAG )    
            
return;
        
if( map_show[x][y] == MINE_ON_SHOW )
            
return;
        
else
            map_show[x][y] 
= MINE_ON_SHOW;

        
if( map[x][y] == 0 )
        
{
            
if( x-1 >= 0 )
            
{
                showMap( x
-1, y );
                
if( y-1 >= 0)        showMap( x-1, y-1 );
                
if( y+1 < map_y)    showMap( x-1, y+1 );
            }

            
if( y-1 >= 0)            showMap( x  , y-1 );
            
if( y+1 < map_y)        showMap( x  , y+1 );
            
if( x+1 < map_x )
            
{
                showMap( x
+1, y );
                
if( y-1 >= 0)        showMap( x+1, y-1 );
                
if( y+1 < map_y)    showMap( x+1, y+1 );
            }

        }

    }

    
    
//重新 开始 游戏
    public void rePlay( int add )
    
{
        
if( add > 0 )
        
{
            map_x     
+= 1;                    //雷区的 行数    // 10 - 15
            map_y     += 1;                    //雷区的 列数    // 10 - 12
            if( map_x >= 15 )    map_x = 15;
            
if( map_y >= 12 )    map_y = 12;
            addMine 
= 0;                    //每次增加的雷数
        }

        
else
            add 
= 0;

        key_x         
= map_x / 2;            //游戏初始时    光标所在雷区的格子位置
        key_y         = map_y / 2;            //游戏初始时    光标所在雷区的格子位置
        mine_num    += add;                    //雷区的雷数    不应该大于雷区的格子总数

        
if(mine_num >= map_x*map_y/2)
            mine_num 
= map_x*map_y/10;    //纠正雷数不能超过雷区的格子数

        flagNum        
= mine_num;            //剩余红旗数
        rightNum    = 0;                //猜对的雷数
        gameState    = STATEPLAY;
        map          
= new int[map_x][map_y];
        map_show     
= new int[map_x][map_y];
        rand        
= new Random( System.currentTimeMillis() );    //用事件作随机数种子的随机数
        isShowInfo    = false;

        
//布雷
        for(int i=0; i<mine_num; i++)        //随机mine_num个雷的位置
        {
            
int x = rand.nextInt( map_x );    //得到 随机数 雷格子的x方向位置
            int y = rand.nextInt( map_y );    //得到 随机数 雷格子的y方向位置
            if( map[x][y] >= 10)            //如果该位置已经是雷了,就要重新布雷
            {    
                i
--;
                
continue;
            }

            map[x][y] 
= 10;                    //否则 将该位置 设定为雷
            
//并在该雷的周围 的雷数都作+1操作
            
//以下判断为 边角判断,防止访问数组越界
            if( x-1 >= 0 )                    
            
{
                map[x
-1][y  ] += 1;
                
if( y-1 >= 0)        map[x-1][y-1+= 1;
                
if( y+1 < map_y)    map[x-1][y+1+= 1;
            }

            
if( y-1 >= 0)        map[x  ][y-1+= 1;
            
if( y+1 < map_y)    map[x  ][y+1+= 1;
            
if( x+1 < map_x )
            
{
                map[x
+1][y  ] += 1;
                
if( y-1 >= 0)        map[x+1][y-1+= 1;
                
if( y+1 < map_y)    map[x+1][y+1+= 1;
            }

        }

    }

}