先画出棋盘界面:
class Gb extends JFrame implements MouseListener ,Runnable {
BufferedWriter bw = null;
BufferedReader br = null;//文档的流
Chess[] chesses = new Chess[17 * 17];//棋子数组,每个网格一个数组
TreeMap tm = new TreeMap<>();//key为序号 value为坐标对应的数组index
TreeMap tmTemp;//读取进度时用,点击加载覆盖tm
static int width = Toolkit.getDefaultToolkit().getScreenSize().width;
static int height = Toolkit.getDefaultToolkit().getScreenSize().height;//获取屏幕的分辨率
boolean isReading = false;
boolean upCanPress = true;
boolean downCanPress = false;//用于读取数据的三个变量
public Gb() {
this.setTitle("五子棋游戏完成版");
this.setSize(900, 800);
this.setLocation((width - 800) / 2, (height - 800) / 2 );
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setVisible(true);
this.repaint();
this.addMouseListener(this);
}
public void paint(Graphics g) {
BufferedImage buf = new BufferedImage(900, 800, BufferedImage.TYPE_INT_RGB);//确定界面的大小,以及涂色方式为RGB
Graphics g1 = buf.createGraphics();
//选项
//背景和棋盘底色
//http://zhongguose.com/#zhizihuang
g1.setColor(new Color(248,232,193));//背景颜色
g1.fill3DRect(0, 0, 900, 800, true);//绘色:(起始坐标X,起始坐标Y,结束坐标X,结束坐标Y)
g1.setColor(new Color(235,177,13));//棋盘色
g1.fill3DRect(60, 60, 680, 680, true);
//棋盘线
for (int i = 80; i <= 720; i += 40) {
g1.setColor(Color.BLACK);
g1.drawLine(80, i, 720, i);
g1.drawLine(i, 80, i, 720);
}
//右上角黑白棋
//用于提醒下一棋子是什么颜色
if(isBlack) {
g1.setColor(Color.BLACK);
g1.fillOval(770, 50, 30, 30);
}else {
g1.setColor(Color.WHITE);
g1.fillOval(770, 50, 30, 30);
}
//音乐键
//图片在资源文件夹中,打开放在项目文件下
try {
imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g1.drawImage(imageMusic,820,47,null);
//功能按键
g1.setFont(new Font("微软雅黑",Font.BOLD,15));
g1.setColor(new Color(158,204,171));
g1.fill3DRect(770, 100, 100, 40,true);
g1.fill3DRect(770, 170, 100, 40,true);
g1.fill3DRect(770, 240, 100, 40,true);
g1.fill3DRect(770, 310, 100, 40,true);
g1.fill3DRect(770, 380, 100, 40,true);
//读取功能,点击之后可以读档,相关按键在功能开启后出现
if(isReading) {
g1.fill3DRect(770, 450, 100, 40,upCanPress);
g1.fill3DRect(770, 520, 100, 40,true);
g1.fill3DRect(770, 590, 100, 40,downCanPress);
}
g1.fill3DRect(770, 660, 100, 40,true);
g1.setColor(Color.BLACK);
g1.drawString("重新开始", 780, 125);
g1.drawString("悔棋", 780, 195);
g1.drawString("认输", 780, 265);
g1.drawString("保存进度", 780, 335);
g1.drawString("读取进度", 780, 405);
if(isReading) {
g1.drawString("上一步", 780, 475);
g1.drawString("加载游戏", 780, 545);
g1.drawString("下一步", 780, 615);
}
g1.drawString("返回大厅", 780, 685);
//棋子填充颜色
for(int i = 0 ; i < 17 ; i++ ) {
for(int j = 0 ; j < 17 ; j++) {
if(chesses[ i + j * 17] != null) {
int realX = 70 + i * 40;
int realY = 70 + j * 40;
//判断棋子颜色 true为黑色 false为白色
if (chesses[i + j * 17].isBlack() == true) {
g1.setColor(Color.BLACK);
g1.fillOval(realX, realY, 20, 20);
}else if (chesses[i + j * 17].isBlack() == false) {
g1.setColor(Color.WHITE);
g1.fillOval(realX, realY, 20, 20);
}
}
}
}
}
棋子类的构建:
public class Chess {
private int x;
private int y;
private int orderNum;
private boolean black;
public Chess() {
}
public Chess(int x, int y, int orderNum, boolean color) {
super();
this.x = x;
this.y = y;
this.orderNum = orderNum;
this.black = color;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
public boolean isBlack() {
return black;
}
public void setBlack(boolean color) {
this.black = color;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Chess other = (Chess) obj;
return black == other.black;
}
}
棋子和棋盘的存储思路:
棋盘是17*17(非标准,当时一拍脑袋决定的),一般来说,用二维数组 chesses[17][17]存储棋盘信息是很符合大脑习惯的,不过二维数组有不方便的地方,直接用chesses[17 * 17]来存储了,其实也很好理解,数组索引对应每个棋子 X + Y * 17(可能需要理解一下),或者由索引反推坐标就是:X = i (数组索引) % 17; Y = i / 17;。
chesses[17 * 17]是棋盘的存储数据,下一个棋子就会在对应索引新建一个棋子对象,并在对应的坐标绘制棋子图像。
落子的存储是用TreeMap,key为落子顺序(1,2,3,4…),value为棋子的一维数组索引。
这样存储的好处是方便读取数据,读取存档的时候很方便。
当然还可以另一个思路:
因为Chess类中申明了落子顺序OrderNum,直接用ArrayList存储落子信息以及棋盘信息,用ArrayList的方法也很方便。
下棋子:
//如果游戏结束,canPlay = false
if(canPlay) {
//下棋子
int x = (e.getX() - 60) / 40;
int y = (e.getY() - 60) / 40;
if (e.getX() >= 80 && e.getY() >= 80 && e.getX() <= 720 && e.getY() <= 720) {
//填充
if(chesses[x + y * 17] == null) {
chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
isBlack = !isBlack;
isSaved = false;
//测试:棋子序号 + 横坐标 + 纵坐标
System.out.println(chesses[x + y * 17].getOrderNum() + "..." +chesses[x + y * 17].getX() + "..." + chesses[x + y * 17].getY());
//test
System.out.println(maxLine(chesses[x + y * 17]));
}
tm.put(chesses[x + y * 17].getOrderNum(), x + y * 17);
this.repaint();
}else {
return;
}
//判断胜负
isWin(maxLine(chesses[x + y * 17]));
}
maxLine方法:
米字形判断,每次落子都判断上下左右斜方向的最长连子数。
基本思路:以横向为例,落子后判断索引±4的最长连子,需要注意一个问题,就是棋子在棋盘的边界,需要加一个判断,如果纵坐标变动了(变成另一排),则重新计算maxLine。如果是竖向判断,则需要监控X坐标有无变动;斜向的话,需要监控Y坐标,如果变化为2,重新计算。
//判断最长连子数
public int maxLine(Chess chess) {
int xLine = 1;
int yLine = 1;
int rightLine = 1;
int leftLine = 1;
int max = 1;
int index = chess.getX() + chess.getY() * 17;
//横向判断
for (int i = index - 4 ; i < index + 4 ; i ++) {
if (i > 0 && i < 17*17 - 1 && (i + 1) / 17 - i / 17 == 0 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 1] != null && chesses[i + 1].equals(chess)) {
xLine++;
}else {
xLine = 1;
continue;
}
max = xLine > max ? xLine : max;
}else {
xLine = 1;
}
}
//纵向判断
if (max == 5) {
return 5;
}else {
for (int i = index - 4 * 17 ; i < index + 4 * 17 ; i += 17) {
if (i > 0 && i < 17*17 - 17 && (i + 17) % 17 - i % 17 == 0 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 17] != null && chesses[i + 17].equals(chess)) {
yLine++;
}else {
yLine = 1;
continue;
}
max = yLine > max ? yLine : max;
}else {
yLine = 1;
}
}
}
//斜向\
if(max == 5) {
return 5;
}else {
for (int i = index - 4 * 18; i < index + 4 * 18 ; i += 18) {
if (i > 0 && i < 17*17 - 18 && (i+ 18) / 17 - i / 17 == 1 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 18] != null && chesses[i + 18].equals(chess)) {
leftLine++;
}else {
leftLine = 1;
continue;
}
max = leftLine > max ? leftLine : max;
}else {
leftLine = 1;
}
}
}
// 斜向/
if(max == 5) {
return 5;
}else {
for (int i = index - 4 * 16; i < index + 4 * 16 ; i += 16) {
if (i > 0 && i < 17*17 - 16 && (i+ 16) / 17 - i / 17 == 1 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 16] != null && chesses[i + 16].equals(chess)) {
rightLine++;
}else {
rightLine = 1;
continue;
}
max = rightLine > max ? rightLine : max;
}else {
rightLine = 1;
}
}
}
return max;
}
isWin方法:
//判断是否胜利,输入最长连子数
public void isWin(int i) {
if (i >= 5) {
canPlay = false;
if(isBlack == true) {
JOptionPane.showMessageDialog(this, "白方胜利");
}else {
JOptionPane.showMessageDialog(this, "黑方胜利");
}
}
}
canPlay用来控制能否下棋,游戏判出胜负后就不能继续落子了,需要点悔棋或者重新开始才能继续。
这样就实现了下棋的基本功能了,至于重新开始,悔棋之类的功能,在单机版很容易实现,之后会po出代码,先将一下稍微有点复杂的读取功能:
先看效果图:
选好文档后,(没有保存文档就乱点然后保存一个)就会弹出“上一步”“加载游戏”“下一步”三个功能按键,“上一步” 和 “下一步”是用来读档的,方便知道落子的顺序,同时也可以选中到想要的进度开始游戏。点击加载就可以从当前画面下的棋盘开始一局游戏了。然后,这三个功能键就会消失,等到再次读取进度的时候再出现。
功能键的画图实现代码在绘图中,功能实现代码如下:
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 380 && e.getY() <= 420) {//这里是坐标
int isYes = JOptionPane.showConfirmDialog(this, "是否读取进度","readload",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
isReading = true;
readLoad();//定义的一个方法,具体在一下代码块里,作用是弹出一个选择文件的框
tmTemp = new TreeMap<>();
for(Map.Entry entry : tm.entrySet()) {
tmTemp.put(entry.getKey(), entry.getValue());
}
}
}
/*
* 读取中的扩展功能
*/
if(isReading) {
canPlay = false;
canPress = false;
int SIZE = tm.size();
int count = tmTemp.size();
//上一步
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 450 && e.getY() <= 490 && upCanPress) {
if(count > 0 ) {
chesses[tmTemp.get(count)] = null;
tmTemp.remove(count-- );
isBlack = !isBlack;
}
//test
System.out.println(tm+" " + SIZE + "\n" +tmTemp + count );
}
//加载
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 520 && e.getY() <= 560) {
isReading = false;
restart();
for(Map.Entry entry : tmTemp.entrySet()) {
tm.put(entry.getKey(), entry.getValue());
chesses[entry.getValue()] = new Chess(entry.getValue() % 17 , entry.getValue() / 17 ,countNum ++ ,isBlack);
isBlack = !isBlack;
isWin(maxLine(chesses[entry.getValue()]));
canPress = true;
}
this.repaint();
}
//下一步
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 590 && e.getY() <= 630 && downCanPress) {
if(count < SIZE ) {
int index = tm.get(++count);
chesses[index] = new Chess(index % 17 , index / 17 ,count,isBlack);
isBlack = !isBlack;
tmTemp.put(count , index);
}
//test
System.out.println(tm+" " + SIZE + "\n" +tmTemp + count );
}
if(count == SIZE) {
downCanPress = false;
}else if(count == 0) {
upCanPress = false;
}else {
downCanPress = true;
upCanPress = true;
}
this.repaint();
}
readLoad方法:
//读取功能,弹出窗口选择保存文档
public void readLoad() {
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
chooser.setFileFilter(filter);
int option = chooser.showOpenDialog(null);
if(option==JFileChooser.APPROVE_OPTION){
File file = chooser.getSelectedFile();
System.out.println(file.getAbsolutePath());
restart();//定义的重开的方法:棋盘清空,存档清空…
try {
br = new BufferedReader(new FileReader(file));
String line = null;
while((line = br.readLine()) != null) {
String[] arr = line.split(" ");
int num = Integer.parseInt(arr[1]);
if(chesses[num] == null) {
chesses[num] = new Chess(num % 17,num / 17,countNum++,isBlack);
isBlack = !isBlack;
tm.put(countNum - 1, num);
this.repaint();
isWin(maxLine(chesses[num]));
}else {
JOptionPane.showMessageDialog(this, "文件读取错误\r\n棋子重复");
break;
}
}
} catch (Exception e1) {
e1.printStackTrace();
}finally {
if(br != null) {
try {
br.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
isSaved = true;
}
}
涉及到IO流和弹窗的类的使用,具体怎么存档可以根据自己开发需要进行修改。
读档的时候,因为可能会多次“上一步”和“下一步”,这里选择再新建一个TreeMap用来复制读档,这个TreeMap的作用就是保存完整的读档文件,具体上一步下一步的操作结果是落实在我们之前就新建好的TreeMap来存档文件,这样可以保证重复“上一步”和“下一步”的操作。
等点击“加载游戏”的时候,再用临时的TreeMap复制进棋盘存储数据的TreeMap中。
等无法进行“上一步”或者“下一步”的操作时,会进行一个判断,此时会将无法执行的按键(比如,才开始读档时,是不能点“下一步”的,或者棋盘已经没有棋子的时候,是不能“上一步”的),按键的样式会变灰一点,具体的实现在绘画代码块中,只需要讲按键方法中最后一个值改为false就行,canPress布尔变量就是定义来干这个事情的。
以下是其他功能的实现代码,同样,绘图的实现在最上面的代码块中。
/*
* 重新开始功能
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
if(canPlay == true) {
if(countNum == 1) {
JOptionPane.showMessageDialog(this, "棋盘并无棋子");
}else {
int isYes = JOptionPane.showConfirmDialog(this, "胜负未分,是否重新开始","restart",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
restart();
}
}
}else {
restart();
}
}
/*
*悔棋功能
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
int isYes = JOptionPane.showConfirmDialog(this, "是否悔棋","take back a move",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
if(tm.get(1) != null) {
canPlay = true;
isBlack = !isBlack;
isSaved = false;
chesses[tm.remove(--countNum)] = null;
this.repaint();
}else {
JOptionPane.showMessageDialog(this, "棋盘没有棋子");
}
}else {
//JOptionPane.showMessageDialog(this, "悔棋失败");
}
}
/*
* 保存功能:落子顺序和坐标数组序号
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
int isYes = JOptionPane.showConfirmDialog(this, "是否保存棋局","saveload",JOptionPane.YES_NO_OPTION);
if(isYes == 0) {
saveLoad();
}
}
/*
* 返回大厅
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
if(isSaved == true) {
int isYes = JOptionPane.showConfirmDialog(this, "是否退出","exit",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
page = 1;
restart();
this.repaint();
}
}else {
int isYes = JOptionPane.showConfirmDialog(this, "棋谱未保存,是否退出前保存?","exit",JOptionPane.YES_NO_CANCEL_OPTION);
if(isYes== 0) {
saveLoad();
page = 1;
restart();
this.repaint();
}else if(isYes == 1){
page = 1;
restart();
this.repaint();
}else {
}
}
}
/*
* 认输功能
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
int isYes = JOptionPane.showConfirmDialog(this, "是否认输","give up",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
canPlay = false;
if(isBlack == true) {
JOptionPane.showMessageDialog(this, "白方胜利");
}else {
JOptionPane.showMessageDialog(this, "黑方胜利");
}
}
}
方法代码:
//重新开始
public void restart() {
canPlay = true;
isBlack = true;
isSaved = false;
chesses = new Chess[17 * 17];
countNum = 1;
tm = new TreeMap();
this.repaint();
}
//保存功能,弹出窗口保存
public void saveLoad() {
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
chooser.setFileFilter(filter);
int option = chooser.showSaveDialog(null);
if(option==JFileChooser.APPROVE_OPTION){
File file = chooser.getSelectedFile();
String fileName = chooser.getName(file);
if(fileName.indexOf(".txt")==-1){
file = new File(chooser.getCurrentDirectory(),fileName+".txt");
System.out.println("renamed");
System.out.println(file.getName());
}
try {
bw = new BufferedWriter(new FileWriter(file));
for(Map.Entry entry : tm.entrySet()) {
bw.write(entry.getKey() + " " + entry.getValue());
bw.newLine();
bw.flush();
}
}catch(IOException ee){
System.out.println("IO异常");
ee.printStackTrace();
}finally{
if(bw != null) {
try {
bw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
isSaved = true;
}
}
这样基本的单机版就算完成了。因为代码是从完整版里面截下来的,比较乱,不过没关系,最后会有全部的代码汇总。
再来一个难实现的功能就是联网了,(为了实现这个功能熬了半个月的夜,当然现在回想起来也不难,自己基础没打好)。
联网的思路有很多,可以建立一个服务器类,棋盘类的代码只需要完成连接服务器的socket就行,当然服务器的构建就会需要花费很多功夫了,毕竟还有匹配(开房间),禁止其他人加入这些功能需要考虑。
我选择了另一个简单的思路:棋盘类继承Runnable类,重写run方法,等点击联网功能键的时候,开出一个支线程,在线程中开启一个 ServerSocket用来接受另一个客户端发来的信息。
等需要发送信息的时候,调用发送方法,新建socket连接对方服务器,即可完成信息的交互。
这里还需要多一嘴,落子的信息很好理解:落子时,只需要将我的落子的棋子在chesses[]数组中的索引给到对方即可,那功能按键呢?
我选择用常量来代替,棋盘落子只需要用到0到17*17 - 1这个区间,那么从17 * 17开始,就可以定义为功能按键的信息。具体代码:
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("执行run()");
String line = null;
try {
server = new ServerSocket(Integer.parseInt(yourPort));
while(true) {
matchsocket = server.accept();
System.out.println("连接成功");
obr = new BufferedReader(new InputStreamReader(matchsocket.getInputStream()));
line = obr.readLine();
int inputNum =Integer.parseInt(line.trim());
System.out.println("读取数据" + inputNum);
translator(inputNum);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void send(int i) {
try {
yoursocket = new Socket(matchIP,Integer.parseInt(matchPort.trim()));
obw = new BufferedWriter(new OutputStreamWriter(yoursocket.getOutputStream()));
obw.write(String.valueOf(i));
obw.newLine();
System.out.println("发送数据" + i);
obw.flush();
obw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void translator(int i) {
if(ONLINE_DISCONNECT == i) {
isFilled = false;
yourIP = null;
yourPort = null;
matchIP = null;
matchPort = null;
obw = null;
obr = null;
yoursocket = null;
matchsocket = null;
server = null;
onlineRestart();
page = 1;
this.repaint();
}else if(ONLINE_RESTART_REQUEST == i) {
//只能判断胜负后才能重开
restartWaitting = true;
JOptionPane.showMessageDialog(this, "对手希望再来一局");
}else if(ONLINE_RESTART_YES == i) {
onlineRestart();
int j = random.nextInt(100);
myNum = ONLINE_DISCONNECT + j + 1;
send(myNum);
}else if(ONLINE_REGRATE_REQUEST == i) {
regrateCanPress = false;
int isYes = JOptionPane.showConfirmDialog(this, "对方请求悔棋,是否同意","take back a move",JOptionPane.YES_NO_OPTION);
if(isYes == 0) {
send(ONLINE_REGRATE_YES);
canPlay = true;
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
chesses[tm.remove(--countNum)] = null;
regrateCanPress = true;
this.repaint();
}else {
send(ONLINE_REGRATE_NO);
regrateCanPress = true;
}
}else if(i > ONLINE_DISCONNECT && i <= ONLINE_DISCONNECT + 100) {
yourTurn = myNum > i ? true : false ;
isBlack = true;
if(yourTurn) {
send(ONLINE_MYTURN);
JOptionPane.showMessageDialog(this, "执先手");
}else {
send(ONLINE_YOURTURN);
JOptionPane.showMessageDialog(this, "执后手");
}
}else if(ONLINE_REGRATE_YES == i) {
canPlay = true;
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
chesses[tm.remove(--countNum)] = null;
regrateCanPress = true;
this.repaint();
}else if(ONLINE_REGRATE_NO == i) {
JOptionPane.showMessageDialog(this, "对方不同意悔棋");
regrateCanPress = true;
}else if(ONLINE_SURRENDER == i) {
canPlay = false;
JOptionPane.showMessageDialog(this, "胜利:对方认输");
}else if(i >= 0 && i < 17 * 17) {
chesses[i] = new Chess(i % 17 , i / 17, countNum++ , isBlack);
onlineIsWin(maxLine(chesses[i]));
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
tm.put(chesses[i].getOrderNum(), i);
this.repaint();
}else if(ONLINE_MYTURN == i) {
yourTurn = false;
isBlack = true;
}else if(ONLINE_YOURTURN == i) {
yourTurn = true;
isBlack = yourTurn;
}else if(ONLINE_WIN == i) {
JOptionPane.showMessageDialog(this, "游戏结束");
}
}
三个方法,分别用来实现[开启服务器][发送信息]和[翻译信息]。
之后,我们只需要在单机版上做出一点调整即可,每次对棋盘做出修改的时候,将相应的信息传递给对方,对方通过translator()方法翻译出来,在棋盘上做出相应的动作即可。
全部的代码在这:
这是主类棋盘类,几乎所有的功能都在这里实现(按理说不应该这样……):
package mygobang;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import javax.imageio.ImageIO;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
class Gb extends JFrame implements MouseListener ,Runnable {
/**
*
*/
private static final long serialVersionUID = 1L;
JLabel label;
BufferedImage imageMusic = null ;
BufferedWriter bw = null;
BufferedReader br = null;//文档的流
Thread onlineThread = new Thread(this);
BufferedReader obr = null;
BufferedWriter obw = null;//联网的流
Chess[] chesses = new Chess[17 * 17];
TreeMap tm = new TreeMap<>();//key为序号 value为坐标对应的数组index
TreeMap tmTemp;//读取进度时用,点击加载覆盖tm
static int width = Toolkit.getDefaultToolkit().getScreenSize().width;
static int height = Toolkit.getDefaultToolkit().getScreenSize().height;
boolean isReading = false;
boolean upCanPress = true;
boolean downCanPress = false;
static boolean hasWindow = false;
static boolean threadIsStart = false;
static String yourIP = null;
static String yourPort = null;
static String matchIP = null;
static String matchPort = null;
private int page = 1;//1 为开始界面。2为单机界面。3为联机界面。
ServerSocket server = null;
Socket yoursocket = null , matchsocket = null;
Random random = new Random();
boolean yourTurn = false;
boolean restartWaitting = false;
boolean restartCanPress = true;
boolean regrateCanPress = true;
public static final int ONLINE_RESTART_REQUEST = 17 * 17;
public static final int ONLINE_RESTART_YES = 17 * 17 + 1;
public static final int ONLINE_REGRATE_REQUEST = 17 * 17 + 2;
public static final int ONLINE_REGRATE_YES = 17 * 17 + 3;
public static final int ONLINE_REGRATE_NO = 17 * 17 + 4;
public static final int ONLINE_SURRENDER = 17 * 17 + 5;
public static final int ONLINE_MYTURN = 17 * 17 + 6;
public static final int ONLINE_YOURTURN = 17 * 17 + 7;
public static final int ONLINE_WIN = 17 * 17 + 8;
public static final int ONLINE_DISCONNECT = 404;
int myNum ;
public Gb() {
this.setTitle("五子棋游戏未完成版");
this.setSize(900, 800);
this.setLocation((width - 800) / 2, (height - 800) / 2 );
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setVisible(true);
this.repaint();
this.addMouseListener(this);
while(true) {
this.music();
}
}
//画棋盘
public void paint(Graphics g) {
BufferedImage buf = new BufferedImage(900, 800, BufferedImage.TYPE_INT_RGB);
Graphics g1 = buf.createGraphics();
/**
* 开始界面
*/
if(page == 1 ) {
//背景和棋盘底色
//http://zhongguose.com/#zhizihuang
g1.setColor(new Color(248,232,193));//背景
g1.fill3DRect(0, 0, 900, 800, true);
g1.setColor(new Color(158,204,171));
g1.fill3DRect(200, 300, 200, 200,true);
g1.fill3DRect(550, 300, 200, 200,true);
BufferedImage game = null;
BufferedImage people = null;
try {
game = ImageIO.read(new File("game1.png"));
people =ImageIO.read(new File("people1.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g1.drawImage(game,260,360,null);
g1.drawImage(people,610,360,null);
try {
imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g1.drawImage(imageMusic,820,47,null);
}else if (page == 2 ) {
//选项
//背景和棋盘底色
//http://zhongguose.com/#zhizihuang
g1.setColor(new Color(248,232,193));//背景
g1.fill3DRect(0, 0, 900, 800, true);
g1.setColor(new Color(235,177,13));//棋盘色
g1.fill3DRect(60, 60, 680, 680, true);
//棋盘线
for (int i = 80; i <= 720; i += 40) {
g1.setColor(Color.BLACK);
g1.drawLine(80, i, 720, i);
g1.drawLine(i, 80, i, 720);
}
//右上角黑白棋
if(isBlack) {
g1.setColor(Color.BLACK);
g1.fillOval(770, 50, 30, 30);
}else {
g1.setColor(Color.WHITE);
g1.fillOval(770, 50, 30, 30);
}
//音乐键
try {
imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g1.drawImage(imageMusic,820,47,null);
//功能按键
g1.setFont(new Font("微软雅黑",Font.BOLD,15));
g1.setColor(new Color(158,204,171));
g1.fill3DRect(770, 100, 100, 40,true);
g1.fill3DRect(770, 170, 100, 40,true);
g1.fill3DRect(770, 240, 100, 40,true);
g1.fill3DRect(770, 310, 100, 40,true);
g1.fill3DRect(770, 380, 100, 40,true);
if(isReading) {
g1.fill3DRect(770, 450, 100, 40,upCanPress);
g1.fill3DRect(770, 520, 100, 40,true);
g1.fill3DRect(770, 590, 100, 40,downCanPress);
}
g1.fill3DRect(770, 660, 100, 40,true);
g1.setColor(Color.BLACK);
g1.drawString("重新开始", 780, 125);
g1.drawString("悔棋", 780, 195);
g1.drawString("认输", 780, 265);
g1.drawString("保存进度", 780, 335);
g1.drawString("读取进度", 780, 405);
if(isReading) {
g1.drawString("上一步", 780, 475);
g1.drawString("加载游戏", 780, 545);
g1.drawString("下一步", 780, 615);
}
g1.drawString("返回大厅", 780, 685);
//棋子填充颜色
for(int i = 0 ; i < 17 ; i++ ) {
for(int j = 0 ; j < 17 ; j++) {
if(chesses[ i + j * 17] != null) {
int realX = 70 + i * 40;
int realY = 70 + j * 40;
//判断棋子颜色 true为黑色 false为白色
if (chesses[i + j * 17].isBlack() == true) {
g1.setColor(Color.BLACK);
g1.fillOval(realX, realY, 20, 20);
}else if (chesses[i + j * 17].isBlack() == false) {
g1.setColor(Color.WHITE);
g1.fillOval(realX, realY, 20, 20);
}
}
}
}
}else/* page == 3 */ {
//选项
//背景和棋盘底色
//http://zhongguose.com/#zhizihuang
g1.setColor(new Color(248,232,193));//背景
g1.fill3DRect(0, 0, 900, 800, true);
g1.setColor(new Color(235,177,13));//棋盘色
g1.fill3DRect(60, 60, 680, 680, true);
//棋盘线
for (int i = 80; i <= 720; i += 40) {
g1.setColor(Color.BLACK);
g1.drawLine(80, i, 720, i);
g1.drawLine(i, 80, i, 720);
}
//右上角黑白棋
if(isBlack) {
g1.setColor(Color.BLACK);
g1.fillOval(770, 50, 30, 30);
}else {
g1.setColor(Color.WHITE);
g1.fillOval(770, 50, 30, 30);
}
//音乐键
try {
imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g1.drawImage(imageMusic,820,47,null);
//功能按键
g1.setFont(new Font("微软雅黑",Font.BOLD,15));
g1.setColor(new Color(158,204,171));
g1.fill3DRect(770, 100, 100, 40,true);
g1.fill3DRect(770, 170, 100, 40,true);
g1.fill3DRect(770, 240, 100, 40,true);
g1.fill3DRect(770, 310, 100, 40,true);
g1.fill3DRect(770, 660, 100, 40,true);
g1.setColor(Color.BLACK);//188,132,168
g1.drawString("重新开始", 780, 125);
g1.drawString("悔棋", 780, 195);
g1.drawString("认输", 780, 265);
g1.drawString("保存进度", 780, 335);
g1.drawString("返回大厅", 780, 685);
//棋子填充颜色
for(int i = 0 ; i < 17 ; i++ ) {
for(int j = 0 ; j < 17 ; j++) {
if(chesses[ i + j * 17] != null) {
int realX = 70 + i * 40;
int realY = 70 + j * 40;
//判断棋子颜色 true为黑色 false为白色
if (chesses[i + j * 17].isBlack() == true) {
g1.setColor(Color.BLACK);
g1.fillOval(realX, realY, 20, 20);
}else if (chesses[i + j * 17].isBlack() == false) {
g1.setColor(Color.WHITE);
g1.fillOval(realX, realY, 20, 20);
}
}
}
}
}
g.drawImage(buf, 0 , 0 ,this);
}
//鼠标点击下棋子
boolean canPlay = true;
boolean isBlack = true;
boolean isSaved = false;
boolean canPress = true;
static boolean isFilled = false;
int countNum = 1;
public void mousePressed(MouseEvent e) {
/*
* 打开音乐,关闭音乐
*/
if(e.getX() >= 820 && e.getX() <= 850 && e.getY() >= 20 && e.getY() <= 80) {
playMusic =!playMusic;
this.repaint();
}
/**
* 三个页面 page 1,2,3
*/
if (page == 1 && !hasWindow) {
//人机按钮
if(e.getX() >= 200 && e.getX() <= 400 && e.getY() >= 300 && e.getY() <= 500) {
page = 2;
this.repaint();
}
//联网对战按钮
if(e.getX() >= 550 && e.getX() <= 750 && e.getY() >= 300 && e.getY() <= 500) {
page = 3;
hasWindow = true;
this.repaint();
new ComponentInWindow();
int i = random.nextInt(100);
myNum = ONLINE_DISCONNECT + i + 1;
System.out.println("随机数是" + myNum);
}
}else if(page == 2 && !hasWindow) {
/*
* 重新开始功能
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
if(canPlay == true) {
if(countNum == 1) {
JOptionPane.showMessageDialog(this, "棋盘并无棋子");
}else {
int isYes = JOptionPane.showConfirmDialog(this, "胜负未分,是否重新开始","restart",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
restart();
}
}
}else {
restart();
}
}
/*
*悔棋功能
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
int isYes = JOptionPane.showConfirmDialog(this, "是否悔棋","take back a move",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
if(tm.get(1) != null) {
canPlay = true;
isBlack = !isBlack;
isSaved = false;
chesses[tm.remove(--countNum)] = null;
this.repaint();
}else {
JOptionPane.showMessageDialog(this, "棋盘没有棋子");
}
}else {
//JOptionPane.showMessageDialog(this, "悔棋失败");
}
}
/*
* 保存功能:落子顺序和坐标数组序号
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
int isYes = JOptionPane.showConfirmDialog(this, "是否保存棋局","saveload",JOptionPane.YES_NO_OPTION);
if(isYes == 0) {
saveLoad();
}
}
/*
* 读取进度功能
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 380 && e.getY() <= 420) {
int isYes = JOptionPane.showConfirmDialog(this, "是否读取进度","readload",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
isReading = true;
readLoad();
tmTemp = new TreeMap<>();
for(Map.Entry entry : tm.entrySet()) {
tmTemp.put(entry.getKey(), entry.getValue());
}
}
}
/*
* 读取中的扩展功能
*/
if(isReading) {
canPlay = false;
canPress = false;
int SIZE = tm.size();
int count = tmTemp.size();
//上一步
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 450 && e.getY() <= 490 && upCanPress) {
if(count > 0 ) {
chesses[tmTemp.get(count)] = null;
tmTemp.remove(count-- );
isBlack = !isBlack;
}
//test
System.out.println(tm+" " + SIZE + "\n" +tmTemp + count );
}
//加载
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 520 && e.getY() <= 560) {
isReading = false;
restart();
for(Map.Entry entry : tmTemp.entrySet()) {
tm.put(entry.getKey(), entry.getValue());
chesses[entry.getValue()] = new Chess(entry.getValue() % 17 , entry.getValue() / 17 ,countNum ++ ,isBlack);
isBlack = !isBlack;
isWin(maxLine(chesses[entry.getValue()]));
canPress = true;
}
this.repaint();
}
//下一步
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 590 && e.getY() <= 630 && downCanPress) {
if(count < SIZE ) {
int index = tm.get(++count);
chesses[index] = new Chess(index % 17 , index / 17 ,count,isBlack);
isBlack = !isBlack;
tmTemp.put(count , index);
}
//test
System.out.println(tm+" " + SIZE + "\n" +tmTemp + count );
}
if(count == SIZE) {
downCanPress = false;
}else if(count == 0) {
upCanPress = false;
}else {
downCanPress = true;
upCanPress = true;
}
this.repaint();
}
/*
* 返回大厅
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
if(isSaved == true) {
int isYes = JOptionPane.showConfirmDialog(this, "是否退出","exit",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
page = 1;
restart();
this.repaint();
}
}else {
int isYes = JOptionPane.showConfirmDialog(this, "棋谱未保存,是否退出前保存?","exit",JOptionPane.YES_NO_CANCEL_OPTION);
if(isYes== 0) {
saveLoad();
page = 1;
restart();
this.repaint();
}else if(isYes == 1){
page = 1;
restart();
this.repaint();
}else {
}
}
}
if(canPlay) {
/*
* 认输功能
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
int isYes = JOptionPane.showConfirmDialog(this, "是否认输","give up",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
canPlay = false;
if(isBlack == true) {
JOptionPane.showMessageDialog(this, "白方胜利");
}else {
JOptionPane.showMessageDialog(this, "黑方胜利");
}
}
}
//下棋子
int x = (e.getX() - 60) / 40;
int y = (e.getY() - 60) / 40;
if (e.getX() >= 80 && e.getY() >= 80 && e.getX() <= 720 && e.getY() <= 720) {
//填充
if(chesses[x + y * 17] == null) {
chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
isBlack = !isBlack;
isSaved = false;
//测试:棋子序号 + 横坐标 + 纵坐标
System.out.println(chesses[x + y * 17].getOrderNum() + "..." +chesses[x + y * 17].getX() + "..." + chesses[x + y * 17].getY());
//test
System.out.println(maxLine(chesses[x + y * 17]));
}
tm.put(chesses[x + y * 17].getOrderNum(), x + y * 17);
this.repaint();
}else {
return;
}
//判断胜负
isWin(maxLine(chesses[x + y * 17]));
}
}else if(page == 3 && !hasWindow) {
System.out.println("点击");
if(!hasWindow && !threadIsStart) {
Thread t = new Thread(this);
t.start();
System.out.println("线程开启");
send(myNum);
threadIsStart = true;
}
// if(threadIsStart) {
//
// }
/*
* 重新开始功能
*/
if(canPress && restartCanPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
if(canPlay) {
JOptionPane.showMessageDialog(this, "胜负未分,请勿放弃!");
}else {
if(!restartWaitting) {
send(ONLINE_RESTART_REQUEST);
restartWaitting = true;
restartCanPress = false;
}else {
int i = random.nextInt(100);
myNum = ONLINE_DISCONNECT + i + 1;
send(ONLINE_RESTART_YES);
send(myNum);
onlineRestart();
}
}
}
/*
*悔棋功能
*/
if(canPress && regrateCanPress && !yourTurn && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
int isYes = JOptionPane.showConfirmDialog(this, "是否悔棋","take back a move",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
if(tm.get(1) != null) {
regrateCanPress = false;
send(ONLINE_REGRATE_REQUEST);
}else {
JOptionPane.showMessageDialog(this, "棋盘没有棋子");
}
}
}
/*
* 保存功能:落子顺序和坐标数组序号
*/
if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
int isYes = JOptionPane.showConfirmDialog(this, "是否保存棋局","saveload",JOptionPane.YES_NO_OPTION);
if(isYes == 0) {
saveLoad();
}
}
/*
* 返回大厅
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
if(isSaved == true) {
int isYes = JOptionPane.showConfirmDialog(this, "是否退出","exit",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
isFilled = false;
yourIP = null;
yourPort = null;
matchIP = null;
matchPort = null;
obw = null;
obr = null;
yoursocket = null;
matchsocket = null;
server = null;
threadIsStart = false;
onlineRestart();
page = 1;
this.repaint();
send(ONLINE_DISCONNECT);
}
}else {
int isYes = JOptionPane.showConfirmDialog(this, "棋谱未保存,是否退出前保存?","exit",JOptionPane.YES_NO_CANCEL_OPTION);
if(isYes== 0) {
saveLoad();
isFilled = false;
yourIP = null;
yourPort = null;
matchIP = null;
matchPort = null;
obw = null;
obr = null;
yoursocket = null;
matchsocket = null;
server = null;
threadIsStart = false;
onlineRestart();
page = 1;
this.repaint();
send(ONLINE_DISCONNECT);
}else if(isYes == 1){
isFilled = false;
yourIP = null;
yourPort = null;
matchIP = null;
matchPort = null;
obw = null;
obr = null;
yoursocket = null;
matchsocket = null;
server = null;
threadIsStart = false;
onlineRestart();
page = 1;
this.repaint();
send(ONLINE_DISCONNECT);
}else {
}
}
}
if(canPlay) {
/*
* 认输功能
*/
if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
int isYes = JOptionPane.showConfirmDialog(this, "是否认输","give up",JOptionPane.YES_NO_OPTION);
if(isYes== 0) {
canPlay = false;
send(ONLINE_SURRENDER);
JOptionPane.showMessageDialog(this, "认输");
}
}
//下棋子
int x = (e.getX() - 60) / 40;
int y = (e.getY() - 60) / 40;
if (yourTurn && e.getX() >= 80 && e.getY() >= 80 && e.getX() <= 720 && e.getY() <= 720) {
//填充
if(chesses[x + y * 17] == null) {
chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
send(x + y * 17);
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
//测试:棋子序号 + 横坐标 + 纵坐标
System.out.println(chesses[x + y * 17].getOrderNum() + "..." +chesses[x + y * 17].getX() + "..." + chesses[x + y * 17].getY());
//test
System.out.println(maxLine(chesses[x + y * 17]));
}
tm.put(chesses[x + y * 17].getOrderNum(), x + y * 17);
this.repaint();
}else {
return;
}
//判断胜负
onlineIsWin(maxLine(chesses[x + y * 17]));
}
}
}
boolean playMusic = true;
public void music() {
try {
AudioInputStream ais = AudioSystem.getAudioInputStream(new File("UGmusic.wav"));
AudioFormat aif = ais.getFormat();
final SourceDataLine sdl;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, aif);
sdl = (SourceDataLine) AudioSystem.getLine(info);
sdl.open(aif);
sdl.start();
FloatControl fc = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
// value可以用来设置音量,从0-2.0
double value = 2;
float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
fc.setValue(dB);
int nByte = 0;
final int SIZE = 1024 * 64;
byte[] buffer = new byte[SIZE];
while (nByte != -1) {// 判断 播放/暂停 状态
if(playMusic) {
nByte = ais.read(buffer, 0, SIZE);
sdl.write(buffer, 0, nByte);
}else {
nByte = ais.read(buffer, 0, 0);
}
}
sdl.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
//判断最长连子数
public int maxLine(Chess chess) {
int xLine = 1;
int yLine = 1;
int rightLine = 1;
int leftLine = 1;
int max = 1;
int index = chess.getX() + chess.getY() * 17;
//横向判断
for (int i = index - 4 ; i < index + 4 ; i ++) {
if (i > 0 && i < 17*17 - 1 && (i + 1) / 17 - i / 17 == 0 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 1] != null && chesses[i + 1].equals(chess)) {
xLine++;
}else {
xLine = 1;
continue;
}
max = xLine > max ? xLine : max;
}else {
xLine = 1;
}
}
//纵向判断
if (max == 5) {
return 5;
}else {
for (int i = index - 4 * 17 ; i < index + 4 * 17 ; i += 17) {
if (i > 0 && i < 17*17 - 17 && (i + 17) % 17 - i % 17 == 0 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 17] != null && chesses[i + 17].equals(chess)) {
yLine++;
}else {
yLine = 1;
continue;
}
max = yLine > max ? yLine : max;
}else {
yLine = 1;
}
}
}
//斜向\
if(max == 5) {
return 5;
}else {
for (int i = index - 4 * 18; i < index + 4 * 18 ; i += 18) {
if (i > 0 && i < 17*17 - 18 && (i+ 18) / 17 - i / 17 == 1 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 18] != null && chesses[i + 18].equals(chess)) {
leftLine++;
}else {
leftLine = 1;
continue;
}
max = leftLine > max ? leftLine : max;
}else {
leftLine = 1;
}
}
}
// 斜向/
if(max == 5) {
return 5;
}else {
for (int i = index - 4 * 16; i < index + 4 * 16 ; i += 16) {
if (i > 0 && i < 17*17 - 16 && (i+ 16) / 17 - i / 17 == 1 ) {
if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 16] != null && chesses[i + 16].equals(chess)) {
rightLine++;
}else {
rightLine = 1;
continue;
}
max = rightLine > max ? rightLine : max;
}else {
rightLine = 1;
}
}
}
return max;
}
//重新开始
public void restart() {
canPlay = true;
isBlack = true;
isSaved = false;
chesses = new Chess[17 * 17];
countNum = 1;
tm = new TreeMap();
this.repaint();
}
//联网重新开始
public void onlineRestart() {
canPlay = true;
isBlack = true;
isSaved = false;
chesses = new Chess[17 * 17];
countNum = 1;
tm = new TreeMap();
restartWaitting = false;
restartCanPress = true;
this.repaint();
}
//判断是否胜利,输入最长连子数
public void isWin(int i) {
if (i >= 5) {
canPlay = false;
if(isBlack == true) {
JOptionPane.showMessageDialog(this, "白方胜利");
}else {
JOptionPane.showMessageDialog(this, "黑方胜利");
}
}
}
public void onlineIsWin(int i) {
if (i >= 5) {
canPlay = false;
if(yourTurn) {
JOptionPane.showMessageDialog(this, "游戏结束!");
}else {
send(ONLINE_WIN);
}
}
}
//保存功能,弹出窗口保存
public void saveLoad() {
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
chooser.setFileFilter(filter);
int option = chooser.showSaveDialog(null);
if(option==JFileChooser.APPROVE_OPTION){
File file = chooser.getSelectedFile();
String fileName = chooser.getName(file);
if(fileName.indexOf(".txt")==-1){
file = new File(chooser.getCurrentDirectory(),fileName+".txt");
System.out.println("renamed");
System.out.println(file.getName());
}
try {
bw = new BufferedWriter(new FileWriter(file));
for(Map.Entry entry : tm.entrySet()) {
bw.write(entry.getKey() + " " + entry.getValue());
bw.newLine();
bw.flush();
}
}catch(IOException ee){
System.out.println("IO异常");
ee.printStackTrace();
}finally{
if(bw != null) {
try {
bw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
isSaved = true;
}
}
//读取功能,弹出窗口选择保存文档
public void readLoad() {
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
chooser.setFileFilter(filter);
int option = chooser.showOpenDialog(null);
if(option==JFileChooser.APPROVE_OPTION){
File file = chooser.getSelectedFile();
System.out.println(file.getAbsolutePath());
restart();
try {
br = new BufferedReader(new FileReader(file));
String line = null;
while((line = br.readLine()) != null) {
String[] arr = line.split(" ");
int num = Integer.parseInt(arr[1]);
if(chesses[num] == null) {
chesses[num] = new Chess(num % 17,num / 17,countNum++,isBlack);
isBlack = !isBlack;
tm.put(countNum - 1, num);
this.repaint();
isWin(maxLine(chesses[num]));
}else {
JOptionPane.showMessageDialog(this, "文件读取错误\r\n棋子重复");
break;
}
}
} catch (Exception e1) {
e1.printStackTrace();
}finally {
if(br != null) {
try {
br.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
isSaved = true;
}
}
public void translator(int i) {
if(ONLINE_DISCONNECT == i) {
isFilled = false;
yourIP = null;
yourPort = null;
matchIP = null;
matchPort = null;
obw = null;
obr = null;
yoursocket = null;
matchsocket = null;
server = null;
onlineRestart();
page = 1;
this.repaint();
}else if(ONLINE_RESTART_REQUEST == i) {
//只能判断胜负后才能重开
restartWaitting = true;
JOptionPane.showMessageDialog(this, "对手希望再来一局");
}else if(ONLINE_RESTART_YES == i) {
onlineRestart();
int j = random.nextInt(100);
myNum = ONLINE_DISCONNECT + j + 1;
send(myNum);
}else if(ONLINE_REGRATE_REQUEST == i) {
regrateCanPress = false;
int isYes = JOptionPane.showConfirmDialog(this, "对方请求悔棋,是否同意","take back a move",JOptionPane.YES_NO_OPTION);
if(isYes == 0) {
send(ONLINE_REGRATE_YES);
canPlay = true;
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
chesses[tm.remove(--countNum)] = null;
regrateCanPress = true;
this.repaint();
}else {
send(ONLINE_REGRATE_NO);
regrateCanPress = true;
}
}else if(i > ONLINE_DISCONNECT && i <= ONLINE_DISCONNECT + 100) {
yourTurn = myNum > i ? true : false ;
isBlack = true;
if(yourTurn) {
send(ONLINE_MYTURN);
JOptionPane.showMessageDialog(this, "执先手");
}else {
send(ONLINE_YOURTURN);
JOptionPane.showMessageDialog(this, "执后手");
}
}else if(ONLINE_REGRATE_YES == i) {
canPlay = true;
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
chesses[tm.remove(--countNum)] = null;
regrateCanPress = true;
this.repaint();
}else if(ONLINE_REGRATE_NO == i) {
JOptionPane.showMessageDialog(this, "对方不同意悔棋");
regrateCanPress = true;
}else if(ONLINE_SURRENDER == i) {
canPlay = false;
JOptionPane.showMessageDialog(this, "胜利:对方认输");
}else if(i >= 0 && i < 17 * 17) {
chesses[i] = new Chess(i % 17 , i / 17, countNum++ , isBlack);
onlineIsWin(maxLine(chesses[i]));
isBlack = !isBlack;
yourTurn = !yourTurn;
isSaved = false;
tm.put(chesses[i].getOrderNum(), i);
this.repaint();
}else if(ONLINE_MYTURN == i) {
yourTurn = false;
isBlack = true;
}else if(ONLINE_YOURTURN == i) {
yourTurn = true;
isBlack = yourTurn;
}else if(ONLINE_WIN == i) {
JOptionPane.showMessageDialog(this, "游戏结束");
}
}
public void send(int i) {
try {
yoursocket = new Socket(matchIP,Integer.parseInt(matchPort.trim()));
obw = new BufferedWriter(new OutputStreamWriter(yoursocket.getOutputStream()));
obw.write(String.valueOf(i));
obw.newLine();
System.out.println("发送数据" + i);
obw.flush();
obw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("执行run()");
String line = null;
try {
server = new ServerSocket(Integer.parseInt(yourPort));
while(true) {
matchsocket = server.accept();
System.out.println("连接成功");
obr = new BufferedReader(new InputStreamReader(matchsocket.getInputStream()));
line = obr.readLine();
int inputNum =Integer.parseInt(line.trim());
System.out.println("读取数据" + inputNum);
translator(inputNum);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
新建一个棋子类:
package mygobang;
public class Chess {
private int x;
private int y;
private int orderNum;
private boolean black;
public Chess() {
}
public Chess(int x, int y, int orderNum, boolean color) {
super();
this.x = x;
this.y = y;
this.orderNum = orderNum;
this.black = color;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
public boolean isBlack() {
return black;
}
public void setBlack(boolean color) {
this.black = color;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Chess other = (Chess) obj;
return black == other.black;
}
}
联网时用到的一个弹窗类:
package mygobang;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class ComponentInWindow extends JFrame implements ActionListener {
JTextField text1,text2,text3,text4;
JButton button;
public ComponentInWindow() {
init();
setVisible(true);
this.setResizable(false);
this.setBounds(0, 0, 320, 150);
this.setTitle("网络链接");
this.setLocation((Gb.width - 320) / 2, (Gb.height - 150) / 2 );
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
button.addActionListener(this);
add(button);
}
public void init() {
setLayout(new FlowLayout()); // 设置布局
add(new JLabel("你的IP:"));
text1 = new JTextField(10);
add(text1);
add(new JLabel("你的端口:"));
text2 = new JTextField(10);
add(text2);
add(new JLabel("对手IP:"));
text3 = new JTextField(10);
add(text3);
add(new JLabel("对手端口:"));
text4 = new JTextField(10);
add(text4);
add(new JLabel("按钮:"));
button = new JButton("开始联机");
add(button);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Gb.yourIP = text1.getText();
Gb.yourPort = text2.getText();
Gb.matchIP = text3.getText();
Gb.matchPort = text4.getText();
if(!Gb.yourIP.equals("") && !Gb.yourPort.equals("") && !Gb.matchIP.equals("") && !Gb.matchPort.equals("")) {
Gb.hasWindow = false;
Gb.isFilled = true;
this.dispose();
}else {
JOptionPane.showMessageDialog(this, "请输入IP和端口");
}
}
}
执行main类:
package mygobang;
public class Run {
public static void main(String[] args) {
new Gb();
}
}
最后给个效果图:
这是第一页,左边单机,右边联网。右上角是背景音乐,点一下可以暂停,同时图片会换。
单机的就是封面了,再来个联网的:
输入完毕后就可以开始游戏了。(局域网)
没有点击开始联机,小窗口是关不掉的,同时棋盘也无法进行操作。
代码源文件和用到的图片音乐都上传到云了,需要自取:
链接: https://pan.baidu.com/s/1gg17ibt9eInbJO8GEgZ5ow
提取码: wfnu
当然,肯定也有做五子棋完成作业的小伙伴,除了特定的IO流弹窗类绘图类这种,尽量看了之后自己手打哈。