连连看大家应该都玩过,不多说直接上一个做好的界面截图吧,所有的功能都在上面的,要做的就只是如何去实现它们了。

差不多就是这个样子。先说一下大致的思路吧。首先编写基本的界面:把什么按钮啊,表格啊什么的都画上去。然后就是编写事件处理类,因为操作使用鼠标,所以加上鼠标监听。然后获取点击的坐标,根据坐标得出图片在数组中的位置。接着创建一个类,实现连连看消除的算法。这样就基本上可以开始游戏了。然后实现排行榜按钮和存档按钮的基本功能。最后加上一个线程类,用于处理倒计时。下面的介绍也基于这个顺序。

界面实现:这个其实没什么好说的,把JFrame的知识用上就好了。考虑到图片的闪烁问题,在界面类中重写paint方法,加上双缓冲(双缓冲不懂的,可以自行百度或者看看我写的Java版本2048)。所以就直接贴代码了。

package com.cbs.look;

public interface LookConfig {
int x=50;//初始x坐标,原来是10
int y=100;//初始y坐标,原来是50
int space=10;//图片间的间隔
int arc=50;//圆角矩形的弧度
int size=60;//图片的大小
int num=9;//图片类型
}
 package com.cbs.look;

 import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints; import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane; /**
* 连连看的主界面类
*
* @author CBS
*
*/
@SuppressWarnings("serial")
public class GameLook extends JFrame implements LookConfig { private int[][] array = new int[8][8];// 数组用于保存界面的信息 JLabel timeJl;// 用于显示剩余时间 public static void main(String[] args) throws InterruptedException {
GameLook g = new GameLook();
g.showUI(); } /**
* 初始化界面
*
* @throws InterruptedException
*/
public void showUI() throws InterruptedException {
setTitle("连连看");
setSize(700, 800);
setDefaultCloseOperation(3);
setLocationRelativeTo(null);
setResizable(true);
setLayout(null); // 添加新游戏按钮
// ImageIcon start = new ImageIcon("res/start.png");
JButton startJB = new JButton("新游戏");
startJB.setBounds(30, 700, 100, 40);
// startJB.setBorderPainted(false);// 设置边框为空
startJB.setFocusable(false);
// startJB.setContentAreaFilled(false);// 设置内容空
this.add(startJB);
// 添加排行榜按钮
JButton save = new JButton("排行榜");
save.setFocusable(false);
save.setBounds(190, 700, 100, 40);
this.add(save); // 添加存档按钮
JButton saveGame = new JButton("存档");
saveGame.setFocusable(false);
saveGame.setBounds(320, 700, 100, 40);
this.add(saveGame); // 添加剩余时间
JLabel jl = new JLabel("Time:");
jl.setFont(new Font("", Font.BOLD, 20));
jl.setBounds(440, 700, 80, 50);
this.add(jl); // 显示剩余时间
timeJl = new JLabel("90");
timeJl.setFont(new Font("", Font.BOLD, 20));
timeJl.setBounds(520, 700, 80, 50);
this.add(timeJl); setVisible(true); GameListener gl = new GameListener();
gl.setFrame(this);
gl.setTimeJl(timeJl);
gl.setArray(array);
saveGame.addActionListener(gl);
startJB.addActionListener(gl);
save.addActionListener(gl); int i=JOptionPane.showConfirmDialog(this, "是否读取上次的存档", "读档",
JOptionPane.YES_NO_OPTION);
if(i==1){
JOptionPane.showMessageDialog(this, "请按新游戏开始游戏吧!");
}else{
GameSave2 gs2=new GameSave2();
CunD c=gs2.opean();
if(c!=null){
array=c.getArray();
gl.setArray(array);
this.addMouseListener(gl);
this.repaint();
TimeOut tt =new TimeOut(timeJl, this, gl);
gl.setTt(tt);
tt.setSeconds(c.getTime());
tt.start();
}else{
JOptionPane.showMessageDialog(this, "读取失败!");
}
} } @Override
public void paint(Graphics g) {
super.paint(g);
buffPaint(g);
} /**
* 使用双缓冲技术解决闪屏问题
*
* @param g传入的画笔对象
*/
public void buffPaint(Graphics g) {
Image i = createImage(space + (size + space) * array[0].length, space
+ (size + space) * array.length);
Graphics2D g2d = (Graphics2D) i.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制背景矩形
g2d.setColor(new Color(210, 180, 140));
g2d.fillRoundRect(0, 0, space + (size + space) * array[0].length, space
+ (size + space) * array.length, arc, arc);
// 绘制背景方格
g2d.setColor(new Color(245, 245, 220));
for (int r = 0; r < array.length; r++) {
for (int c = 0; c < array[r].length; c++) {
g2d.fillRect(space + (size + space) * c, space + (size + space)
* r, size, size);
}
}
// 绘制图片
g2d.setColor(Color.BLUE);
g2d.setFont(new Font("宋体", Font.BOLD, 30));
for (int r = 0; r < array.length; r++) {
for (int c = 0; c < array[r].length; c++) {
if (array[r][c] != 0) {
ImageIcon icon = new ImageIcon("res/" + array[r][c]
+ ".jpg");
Image image = icon.getImage();
g2d.drawImage(image, space + (size + space) * c, space
+ (size + space) * r, size, size, null);
}
}
}
g.drawImage(i, x, y, this);
}
}

事件处理类:鼠标的事件处理主要负责的是记录两次点击的坐标,然后判断是否能够把两个图片消除,如果可以消除图片把对应的数组位置的数置为0,然后重绘画板,如果不行同样重绘画板消除选框及连线。动作的事件处理主要负责实现不同的按钮的功能。

package com.cbs.look;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Random; import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.plaf.FontUIResource;
/**
* 事件处理类
* @author CBS
*/
public class GameListener extends MouseAdapter implements LookConfig,
ActionListener {
// 用于控制坐标的获取
private boolean flag = true;
private int r1, c1, r2, c2;// 对应数组的下标位置
private int x1, y1, x2, y2;// 鼠标点击的坐标
private int array[][];// 保存数组
private JFrame frame;// 用于获取窗体对象,调用Repaint方法
private Graphics2D g;// 画笔对象
JLabel timeJl;// 用于显示剩余时间
TimeOut tt ;// 倒计时线程类 private int x;// 保存画框的顶点x坐标
private int y;// 保存画框的顶点y坐标 public TimeOut getTt() {
return tt;
} public void setTt(TimeOut tt) {
this.tt = tt;
} public void setTimeJl(JLabel timeJl) {
this.timeJl = timeJl;
} public void setFrame(JFrame frame) {
this.frame = frame;
g = (Graphics2D) frame.getGraphics();
} public void setArray(int[][] array) {
this.array = array;
} @Override
public void mousePressed(MouseEvent e) {
// 获取坐标
if (flag) {
x1 = e.getX() - 40;
y1 = e.getY() - 50;
flag = false;
if (y1 / (size + space) - 1 >= array.length)
r1 = array.length - 1;
else if (y1 / (size + space) - 1 < 0)
r1 = 0;
else
r1 = y1 / (size + space) - 1;
if (x1 / (size + space) >= array[0].length)
c1 = array[0].length - 1;
else
c1 = x1 / (size + space);
g.setColor(Color.RED);
g.setStroke(new BasicStroke(5));
x = space + space + c1 * (size + space) + 40;
y = size + r1 * (size + space) + 50;
g.drawRect(x, y, size, size);
} else {
x2 = e.getX() - 40;
y2 = e.getY() - 50;
flag = true;
if (y2 / (size + space) - 1 >= array.length)
r2 = array.length - 1;
else if (y1 / (size + space) - 1 < 0)
r1 = 0;
else
r2 = y2 / (size + space) - 1;
if (x2 / (size + space) >= array[0].length)
c2 = array[0].length - 1;
else
c2 = x2 / (size + space);
g.setColor(Color.RED);
g.setStroke(new BasicStroke(4));
x = space + space + c2 * (size + space) + 40;
y = size + r2 * (size + space) + 50;
g.drawRect(x, y, size, size);
}
GameUtil gu = new GameUtil(this.frame);
if (array[r1][c1] == array[r2][c2] && flag && !(r1 == r2 && c2 == c1)
&& (array[r1][c1] != 0 || array[r2][c2] != 0)) {
if (gu.wuZhe(r1, c1, r2, c2, array)) {
array[r1][c1] = 0;
array[r2][c2] = 0;
g.setColor(Color.PINK);
g.drawLine(2 * space + size / 2 + c2 * (size + space) + 40,
size + size / 2 + r2 * (size + space) + 50, 2 * space
+ size / 2 + c1 * (size + space) + 40, size
+ size / 2 + r1 * (size + space) + 50); } else if (gu.yiZhe(r1, c1, r2, c2, array)) {
array[r1][c1] = 0;
array[r2][c2] = 0;
g.setColor(Color.PINK);
g.drawLine(2 * space + size / 2 + gu.getPath().get(0).y
* (size + space) + 40, size + size / 2
+ gu.getPath().get(0).x * (size + space) + 50, 2
* space + size / 2 + c1 * (size + space) + 40, size
+ size / 2 + r1 * (size + space) + 50);
g.drawLine(2 * space + size / 2 + gu.getPath().get(0).y
* (size + space) + 40, size + size / 2
+ gu.getPath().get(0).x * (size + space) + 50, 2
* space + size / 2 + c2 * (size + space) + 40, size
+ size / 2 + r2 * (size + space) + 50); } else if (gu.erZhe(r1, c1, r2, c2, array)) {
array[r1][c1] = 0;
array[r2][c2] = 0;
g.setColor(Color.PINK);
g.drawLine(2 * space + size / 2 + gu.getPath().get(1).y
* (size + space) + 40, size + size / 2
+ gu.getPath().get(1).x * (size + space) + 50, 2
* space + size / 2 + c1 * (size + space) + 40, size
+ size / 2 + r1 * (size + space) + 50);
// path的下标为一的位置要减一,因为数组扩大了
g.drawLine(2 * space + size / 2 + (gu.getPath().get(0).y - 1)
* (size + space) + 40, size + size / 2
+ (gu.getPath().get(0).x - 1) * (size + space) + 50, 2
* space + size / 2 + gu.getPath().get(1).y
* (size + space) + 40, size + size / 2
+ gu.getPath().get(1).x * (size + space) + 50);
g.drawLine(2 * space + size / 2 + (gu.getPath().get(0).y - 1)
* (size + space) + 40, size + size / 2
+ (gu.getPath().get(0).x - 1) * (size + space) + 50, 2
* space + size / 2 + c2 * (size + space) + 40, size
+ size / 2 + r2 * (size + space) + 50);
} //实现消除控制重绘的刷新时间
Thread t=new Thread();
try {
t.sleep(100);
frame.repaint();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} if (isWin(array)) {
tt.setFlag(false);
frame.removeMouseListener(this);
JOptionPane.showMessageDialog(frame, "恭喜你,"
+ "你赢了!!请点击新游戏开始新一局");
int i = JOptionPane.showConfirmDialog(frame, "是否记录将你的信息记入排行榜",
"排行榜", JOptionPane.YES_NO_OPTION);
if (i == 0) {
String str = JOptionPane.showInputDialog(frame, "请输入你的名字",
"排行榜", JOptionPane.YES_NO_OPTION);
int time=90-tt.getSeconds();
User u = new User(str, time);
GameSave gs = new GameSave();
gs.save(u);
}
}
}
//未实现消除,重绘去掉线条
if (flag) {
Thread t=new Thread();
try {
t.sleep(100);
frame.repaint();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
} // 按钮动作监听
public void actionPerformed(ActionEvent e) {
String str = e.getActionCommand(); if ("新游戏".equals(str)) {
for (int r = 0; r < array.length; r++)
for (int c = 0; c < array[r].length; c++)
if (array[r][c] != 0) {
array[r][c] = 0;
}
if(tt!=null){
if(tt.isFlag()){
frame.removeMouseListener(this);
tt.setFlag(false);
}
}
randomData();
frame.repaint();
frame.addMouseListener(this);
// 启动线程
tt = new TimeOut(timeJl, frame, this);
if(!tt.isFlag())
tt.setFlag(false);
tt.start();
}
if ("排行榜".equals(str)) {
GameSave gs = new GameSave();
List<User> list = gs.opean();
for (int i = 0; i < list.size(); i++) {
int flag = i;
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).getTime() > list.get(j).getTime())
flag = j;
}
if (flag != i) {
User u1 = list.get(i);
User u2 = list.get(flag);
list.set(i, u2);
list.set(flag, u1);
}
}
JFrame jf = new JFrame();
jf.setTitle("排行榜");
jf.setDefaultCloseOperation(2);
jf.setSize(300, 500);
FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
jf.setLayout(fl);
jf.setLocationRelativeTo(null);
for (int i = 0; i < list.size(); i++) {
JLabel jl = new JLabel(list.get(i).toString());
jl.setFont(new FontUIResource("楷体", Font.BOLD, 20));
jf.add(jl);
}
jf.setVisible(true);
}
if("存档".equals(str)){
System.out.println(23333);
GameSave2 gs2=new GameSave2();
int time=tt.getSeconds();
CunD c=new CunD(array, time);
boolean is=gs2.save(c);
if(is)
JOptionPane.showMessageDialog(frame, "存档成功!");
else
JOptionPane.showMessageDialog(frame, "存档失败!");
}
} /**
* 生成随机数字
*/
public void randomData() {
Random random = new Random();
int r1, r2, c1, c2;
for (int i = 0; i < array.length * array[0].length / 2; i++) {
do {
r1 = random.nextInt(array.length);
c1 = random.nextInt(array[r1].length);
} while (array[r1][c1] != 0);
array[r1][c1] = random.nextInt(num) + 1;
do {
r2 = random.nextInt(array.length);
c2 = random.nextInt(array[r2].length);
} while (array[r2][c2] != 0);
array[r2][c2] = array[r1][c1];
}
}
//遍历数组,判断输赢
public boolean isWin(int[][] array) {
for (int r = 0; r < array.length; r++)
for (int c = 0; c < array[r].length; c++)
if (array[r][c] != 0)
return false;
return true;
}
}

这里的图片我使用的是直接绘制图片,而不是通过按钮,所以坐标的判断有些麻烦。数组的下标取得是通过鼠标坐标整除方格的边长加间隔,然后由数组下标取得屏幕坐标则相反。初始数据是由randomData方法生成,不同的数字对应不同的图片。

连连看的算法:这里我使用的算法是比较容易理解和实现的分类算法,据说还有一种比较厉害的算法叫广度优先搜索,那个我不会,所以就只能用这种了。先说说连连看的规则吧,就是用不超过两次拐弯的直线能够相连就能够消除。这样分类算法就很好理解了,按照消除要拐弯的次数分为无折相连,一折相连和二折相连。首先是无折相连这个很好理解,要么他们左右相邻或者是上下相邻,要么就是同一行,两个方快中间没有阻隔或者是同一列中间没有阻隔。

就像上面的图,左边是不相邻的,右边是相邻的,这两种都属于无折的相连。然后是一折的相连。一折的相连就是拐一个弯,先看看示意图把:

其实无论是哪种情况,能够实现一折相连的方块都是在矩形的两个对顶角,所以只要判断矩形的另外两个对顶角是否能够实现无折相连就行了。最后是二折相连,同样是先看示意图:

二折的情况看似复杂,其实只要在水平方向上和垂直方向上分别进行遍历,如果是空格就判断这个空格是否能够和另一个格子一折相连就行了。其实整个算法有点像是递归,一折调用无折,二折调用一折。算法的思路大概就是这样。然后就上代码吧:

package com.cbs.look;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List; import javax.swing.JFrame; /**
* 核心算法
* 判断两个方块是否联通
* @author CBS
*
*/
public class GameUtil implements LookConfig {
//path主要是记录下相连的数组的位置,为了方便实现连线的功能
private List<Point> path=new ArrayList<Point>(); public List<Point> getPath() {
return path;
} public GameUtil(JFrame frame) {
} /**
* 无折算法,无折的情况,要么同行,判断列是否连通;要么同列判断行是否连通
*
* @param r1第一个方块行下标
* @param c1第一个方块列下标
* @param r2第二个方块行下标
* @param c2第二个方块列下标
* @param array用于保存数组的信息
* @return 如果能够连通返回TRUE,or返回FALSE
*/
public boolean wuZhe(int r1, int c1, int r2, int c2, int[][] array) { if (r1 != r2 && c1 != c2)
return false;
// 如果两点的x坐标相等,则在水平方向上扫描
if (r1 == r2) {
if (c1 == c2 - 1 || c2 == c1 - 1)// 列相邻
return true;
for (int i = Math.min(c1, c2) + 1; i < Math.max(c2, c1); i++)
if (array[r1][i] != 0)
return false;
}
// 如果两点的y坐标相等,则在竖直方向上扫描
else if (c1 == c2) {
if (r1 == r2 - 1 || r2 == r1 - 1)// 行相邻
return true;
for (int i = Math.min(r1, r2) + 1; i < Math.max(r2, r1); i++)
if (array[i][c1] != 0)
return false;
}
return true;
} /**
* 一折算法,无论是哪种情况下,都只需要判断对角的r1,c2和r2,c1和两点是否连通
*
* @param r1第一个方块行下标
* @param c1第一个方块列下标
* @param r2第二个方块行下标
* @param c2第二个方块列下标
* @param array 用于保存数组的信息
* @return 如果能够连通返回TRUE,or返回FALSE
*/
public boolean yiZhe(int r1, int c1, int r2, int c2, int[][] array) { // 如果属于0折的情况,直接返回FALSE
if (r1 == r2 || c1 == c2)
return false;
// 测试对角点1
if (array[r1][c2] == 0) {
boolean test1 = wuZhe(r1, c1, r1, c2, array);
boolean test2 = test1 ? wuZhe(r1, c2, r2, c2, array) : test1;
if (test1 && test2){
path.add(new Point(r1,c2));
return true;
}
}
// 测试对角点2
if (array[r2][c1] == 0) {
boolean test1 = wuZhe(r1, c1, r2, c1, array);
boolean test2 = test1 ? wuZhe(r2, c1, r2, c2, array) : test1;
if (test1 && test2){
path.add(new Point(r2,c1));
return true;
}
}
return false;
} /**
* 二折算法
*
* @param r1第一个方块行下标
* @param c1第一个方块列下标
* @param r2第二个方块行下标
* @param c2第二个方块列下标
* @param array用于保存数组的信息
* @return 如果能够连通返回TRUE,or返回FALSE
*/
public boolean erZhe(int r1, int c1, int r2, int c2, int[][] array) {
//在原来数组的基础上扩大一圈,用于判断边界的方格
int[][] newArray = new int[array.length + 2][array[0].length + 2];
for (int r = 0; r < array.length; r++) {
for (int c = 0; c < array[r].length; c++) {
newArray[r + 1][c + 1] = array[r][c];
}
} //判断是否二折连接 // 向下垂直遍历
for (int i = r1 + 2; i < newArray.length; i++) {
if (newArray[i][c1+1] == 0 ) {
if(yiZhe(r2+1, c2+1, i, c1+1, newArray)){
path.add(new Point(i-1, c1));
return true;
}
}else
break;
}
// 向上垂直遍历
for (int i = r1 ; i > -1; i--) {
if (newArray[i][c1+1] == 0 ){
if(yiZhe(i, c1+1, r2+1, c2+1, newArray)){
path.add(new Point(i-1, c1));
return true;
}
}else
break; }
// 向右水平遍历
for (int i = c1 + 2; i < newArray[0].length; i++) {
if (newArray[r1+1][i] == 0 ){
if( yiZhe(r2+1, c2+1, r1+1, i, newArray)){
path.add(new Point(r1,i-1));
return true;
}
}else
break; }
// 向左水平遍历
for (int i = c1 ; i > -1; i--) {
if (newArray[r1+1][i] == 0 ) {
if(yiZhe(r1+1, i, r2+1, c2+1, newArray)){
path.add(new Point(r1,i-1));
return true;
}
}else
break;
}
return false; } }

还有一个要说一下就是上面第一张图的那种二折情况,如果这种二折情况处于数组的边界的话,在使用上面的二折算法判断就会因为数组的问题,永远不可能实现相连。解决的方法就是:把数组扩大,在整个的数组外边加上一层,也就是说原来4*4变成6*6。把所有除原来4*4之外的位置的值都设为0,这样就能够遍历到了。不过在判断的时候要注意使用的是新数组的坐标还是旧数组的坐标(把新数组的坐标行下标和列下标都减一就是旧数组的行下标和列下标)。能够消除就意味着你的连连看游戏已经可以玩了,剩下的就是一些优化的问题了。

排行榜和存档功能的实现:这两个功能放一起讲,因为都用到了Java的IO的一些知识。这里主要用到的是Java中的FileOutputStream和FileInputStream这两个类。FileOutputStream这个类是把内存中的数据输出到外部的存储设备,主要的方法就是write(向目标文件一次写入一个字节)和close(关闭输出流);FileInputStream则是把外部的数据输入到内存中供程序使用,主要的方法就是read(从目标文件一次读取一个字节)和close(关闭输入流)。存档的思路就是:把当前的数组情况和时间记录下来(使用一个类来保存这两个信息),保存到一个文件中,下次打开游戏的时候询问用户是否载入进度。排行榜的话是如果玩家获胜,就记录下玩家的姓名和通关用时(同样使用一个类),保存到文件中。下面是代码,把存档和排行榜的相关类都放进去了:

 package com.cbs.look;

 /**
* 存档
*
* @author CBS
*/ public class CunD {
private int array[][];
private int time; public CunD() {
} public CunD(int[][] array, int time) {
this.array = array;
this.time = time;
} public int[][] getArray() {
return array;
} public void setArray(int[][] array) {
this.array = array;
} public int getTime() {
return time;
} public void setTime(int time) {
this.time = time;
} }
package com.cbs.look; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; /**
* 用于保存游戏存档的信息
*
* @author CBS
*/
public class GameSave2 { public boolean save(CunD d) { String path = "src/com/cbs/look/info.txt"; try { OutputStream os = new FileOutputStream(path);
os.write(d.getTime());
for (int r = 0; r < d.getArray().length; r++) {
for (int c = 0; c < d.getArray()[0].length; c++) {
os.write(d.getArray()[r][c]);
}
}
os.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} return false;
} public CunD opean() {
String path = "src/com/cbs/look/info.txt";
try {
InputStream is = new FileInputStream(path);
int time = is.read();
int array[][]=new int[8][8]; for (int i = 0; i < array.length; i++)
for (int j = 0; j < array[0].length; j++)
array[i][j] = is.read();
CunD c=new CunD(array,time);
is.close();
return c; } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
package com.cbs.look; /**
* 记录排行榜的用户信息的类
*
* @author CBS
*
*/
public class User { private String name;//用户名 private int time;//记录用户通过所用时间 public User() {
} public User(String name, int time) {
this.name = name;
this.time = time;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getTime() {
return time;
} public void setTime(int time) {
this.time = time;
} @Override
public String toString() {
String str=name+"用时为:"+time;
return str;
} }
package com.cbs.look; import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List; /**
* 用于保存游戏排行榜信息
*
* @author CBS
*/
public class GameSave { public boolean save(User user) { String path = "src/com/cbs/look/save3.txt"; try {
List<User> array = opean();
if(array!=null){
array.add(user);
for (int i=0;i<array.size()-1;i++) {
int flag=i;
for(int j=i+1;j<array.size();j++){
if(array.get(i).getTime()>array.get(j).getTime())
flag=j;
}
if(flag!=i){
User u1=array.get(i);
User u2=array.get(flag);
array.set(i, u2);
array.set(flag, u1);
}
}
}else{
array=new ArrayList<User>();
array.add(user);
} OutputStream os = new FileOutputStream(path);
DataOutputStream dos=new DataOutputStream(os);
//先写入有几条信息
os.write(array.size());
for (int i = 0; i < array.size(); i++) {
User u = array.get(i);
dos.writeByte(u.getName().getBytes().length);
dos.write(u.getName().getBytes());
dos.writeInt(u.getTime());
}
os.close();
dos.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} return false;
} public List<User> opean() {
String path = "src/com/cbs/look/save3.txt";
try {
InputStream is = new FileInputStream(path);
DataInputStream dis = new DataInputStream(is); //读取有几条信息
int size=is.read();
List<User> list=new ArrayList<User>(); if(size!=-1){
for(int i=0;i<size;i++){
byte bsize=dis.readByte();
byte[] b=new byte[bsize];
is.read(b);
int time=dis.readInt();
User u=new User(new String(b),time);
list.add(u);
}
}
dis.close();
is.close();
return list; } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

倒计时的实现:倒计时其实很简单使用一个线程就好(如果线程不了解请自行百度或者学习,不用很了解,知道就行,只是为了做个倒计时罢了),每次线程休眠一秒就让时间减一,然后把时间在窗体中输出就行了。也没什么可说的,上代码咯:

package com.cbs.look;
/**
* 线程类,控制时间
*/
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane; public class TimeOut extends Thread {
private int seconds=90;//游戏时间
private JFrame frame;//主窗体对象
private JLabel jl;//倒计时标签
private boolean flag = true;// 控制线程结束
private GameListener gl;//事件处理类 // 在别的类中控制线程的关闭
public void setFlag(boolean flag) {
this.flag = flag;
} public boolean isFlag() {
return flag;
} public int getSeconds() {
return seconds;
} public void setSeconds(int seconds) {
this.seconds = seconds;
} public TimeOut(JLabel jl, JFrame frame, GameListener gl) {
this.jl = jl;
this.frame = frame;
this.gl = gl;
} @Override
public void run() {
// seconds = 90;
jl.setText(seconds + "");
while (seconds-- > 0 && flag) {
jl.setText(seconds + "");
if (seconds == 0) {
JOptionPane.showMessageDialog(frame, "不好意思,时间用光了,请开始新游戏");
frame.removeMouseListener(gl);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

这样所有的东西也就都讲完了,剩下的就是如何把它们组织在一起形成你的东西了。

Java版连连看的更多相关文章

  1. Android 绿豆蛙版连连看(简陋版)

    (里面有六张绿豆蛙的图片) 1.选中会有红色框 2.可以直线连(横竖相邻或是横竖间隔空格) 3.可以拐一次弯连接 4.可以拐两次弯连接 5.连接时会有线显示 6.绿色代表进度条,蓝色代表时间条 imp ...

  2. ArcGIS Server 10 Java 版的Rest服务手动配置方法

    Java版的Manager中发布的服务默认只发布了该服务的SOAP接口,而REST接口需要用户在信息服务器,如Tomcat. Apache.WebLogic等中手工配置.由于在Java版的Server ...

  3. PetaPojo —— JAVA版的PetaPoco

    背景 由于工作的一些原因,需要从C#转成JAVA.之前PetaPoco用得真是非常舒服,在学习JAVA的过程中熟悉了一下JAVA的数据组件: MyBatis 非常流行,代码生成也很成熟,性能也很好.但 ...

  4. 【转载】java版打字练习软件

    网上找到一个java版的打字的游戏 import java.applet.Applet; import java.applet.AudioClip; import java.awt.Dimension ...

  5. 微博地址url(id)与mid的相互转换 Java版

    原理: 新浪微博的URL都是如:http://weibo.com/2480531040/z8ElgBLeQ这样三部分. 第一部分(绿色部分)为新浪微博的域名,第二部分(红色部分)为博主Uid,第三部分 ...

  6. JCEF3——谷歌浏览器内核Java版实现(一):使用jawt获取窗体句柄

    前言 最近一段时间研究谷歌浏览器内核.谷歌浏览器内核一直开源,并维护更新,它的开源项目中内核更新速度和Chrome浏览器版本更新进度一样!而且它不同于WebKit(值得一题的是谷歌浏览器已不使用Web ...

  7. Selenium关键字驱动测试框架Demo(Java版)

    Selenium关键字驱动测试框架Demo(Java版)http://www.docin.com/p-803493675.html

  8. 第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容

    第一部分:微信JS-SDK介绍 微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统 ...

  9. 第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...

随机推荐

  1. Java集合分析

    Java集合分析 前言 从开始接触Java的时候, 就在强调的一个重要版块, 集合. 终于能够开始对它的源码进行分析, 理解, 如果不懂得背后的思想, 那么读懂代码, 也仅仅是读懂了 if else ...

  2. 获取客户端登录ip地址

    request方法客户端IP: request.getRemoteAddr()  输出:192.168.0.106 客户端主机名:request.getRemoteHost()输出:abc 在JSP里 ...

  3. weex 启动 ios 模拟器

    前提需要的安装 node npm weex-toolkit cocoaPods 1. 创建weex工程 weex create helloWolrd 2. 进入helloWolrd文件夹安装依赖,我用 ...

  4. 【转】shell中如何判断一个变量是否为空

    判断一个脚本中的变量是否为空,我写了一个这样的shell脚本: #!/bin/sh #filename: test.sh para1= if [ ! -n $para1 ]; then echo &q ...

  5. Sql Server的艺术(四) SQL多表查询

    表的基本连接 SQL的一个重要特性就是能通过JOIN关键词,从多个交叉表中查询.分析数据. 连接表的目的 在关系数据库中,数据表设计的一个重要原则就是要避免冗余性. 减少了冗余信息,节省了数据库存储空 ...

  6. 【django之用户认证】

    一.auth模块 模块导入 from django.contrib import auth 主要方法如下: 1 .authenticate()    提供了用户认证,即验证用户名以及密码是否正确,一般 ...

  7. 读Ghost博客源码与自定义Ghost博客主题

    我使用的Ghost博客一直使用者默认的Casper主题.我向来没怎么打理过自己博客,一方面认为自己不够专业,很难写出质量比较高的文字:另一方面认为博客太耗时间,很容易影响正常的工作内容.最近公司即将搬 ...

  8. vue2.0开发时导入组件时出错

    导入自定义组件时出现了如下错误 ERROR Failed to compile with 1 errors 12:35:41 This dependency was not found: * comp ...

  9. CF154D. Flatland Fencing [博弈论 对称 平局]

    传送门 题意: 背景是$knights' tournament$,好棒的样子! 这道题不一样很恶心的地方就是有平局的存在 首先判断能不能一步杀 不能的话,如果可以走$0$步或者$a,b$一负一正那么一 ...

  10. BZOJ 3669: [Noi2014]魔法森林 [LCT Kruskal | SPFA]

    题目描述 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…, ...