JAVA联机版五子棋 您所在的位置:网站首页 五子棋编程代码java JAVA联机版五子棋

JAVA联机版五子棋

2023-09-27 16:45| 来源: 网络整理| 查看: 265

第一次写博客,排版啥的都比较乱,大家不要嫌弃啊。

所谓暴力开发,其实是啥也不会,硬怼哈哈哈。只是刚学会一点JAVA编程,参考网络资源写了个联机版五子棋(支持单机),来这里记录一下学习过程,而且我也是靠别人的分享才能写出来,所以本着互相帮助的原则,分享给大家,抛砖引玉,共同进步。

作为一个半路出家,正在学习道路上的渣渣,摸索着写了这样一个乱乱的五子棋,很多地方都不正规,比如因为没有系统学过GUI,对话框都用的JOptionPane组件,模式选择对话框也放在main方法里,算是偷懒吧。。

正如标题里写的“一个类一个main”,写的时候为了快速且方便,都放在一个类里了,因此大家想要测试的话,直接整个复制就行了,没啥黑科技,JDK自带的包就能运行。

因为个人的恶趣味,本五子棋起名“老铁互怼五子棋”,当然各位可以自己随意修改。

转载也请随意,标明出处就行了(如果感觉有用,点个赞就是最好的支持啦)。

游戏打开后是这个界面,直接选择游戏模式,不能半途选别的模式,如果要更换,需要重启游戏(不然老出线程异常,所以直接砍掉这个功能哈哈哈。。。)。

游戏打开界面

选择“是”则为联机版,“否”或关闭对话框为单机版。

联机版需要输入对方的主机名或IP(局域网)。

输入对方主机名或IP

没有集成GUI,所以主机名和端口号都需要分别输入(偷个懒)。

这里输入本机的端口号。

输入己方端口号

输入对方端口号,双方端口号需要互相对应。

输入对方端口号

以上步骤都没错且网络没问题,则连接成功,点击确定可以开始游戏,先落子的为黑棋。

说明一下,如果输入的主机名或地址为本机,且输入的端口号一致,则为单机模式(预防我预设的9653端口号被占,这种方式可以自己设置端口号)。

因为使用了线程,可以本机打开两个游戏界面,联机模式下可以左右互搏(狗头),也是实现单机模式的原理(好像也是开发中线程老是占用的罪魁祸首,难受)。

联机模式确认

如果一开始选择了“否”或关闭对话框,则为单机模式,显示下图所示对话框,点击确定开始游戏。

单机模式确认

游戏界面如下,刚落下的棋子有外框显示。

游戏开始

代码如下(注释可能有点啰嗦。。):

package netWuZiQiDemo; //打开游戏选择"是"则为联机模式,选择"否"或关闭对话框则为单机模式,默认端口号9653(不与常用端口号冲突) //本版本联机需要输入对方主机名或IP地址,并且需要双方端口号对应相反 //本版本联机模式,双方轮流执黑 //如果主机名或IP为自己主机(或没有输入),则转为单机模式,此时需两个端口号一致 //为了解决线程异常问题(水平有限...),特推出此魔改版,打开游戏必须选择其中一种模式(联机或单机),要想更换,则要关闭游戏重新打开 import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; /** * 老铁互怼五子棋1.0 * @author Arno-Woke * */ public class NetLTWZQDemo extends JPanel implements MouseListener, Runnable { int x;// 落子处棋盘坐标 int y; int[][] chess = new int[12][12];// 棋盘大小为12x12 int ex;// 传送鼠标点击坐标 int ey; boolean me = true;// 初始化为己方先落黑子 boolean one = true;// 控制联机双方每次只能一个人下子 static boolean start = false;// 默认打开游戏不能下子,需先选择游戏模式 static InetAddress inetAddress;// 客户端确认服务端的IP地址 static String UsernameOrIp;// 联机对方主机名或IP地址 // 客户端和服务端端口号一致则为单机版五子棋 static int clientPort;// 设置客户端端口 static int serverPort;// 设置服务端端口 static boolean danJi;// 选择单机模式,则程序只判断一次胜负,防止对局结束后无法落子,若联机模式,则判断两次胜负 static Thread t;// 声明一个线程 private static final long serialVersionUID = 1L;//emmm 没什么用,提示要有一个就放一个吧 public static void main(String[] args) { JFrame jFrame = new JFrame("老铁互怼五子棋"); jFrame.setBounds((Toolkit.getDefaultToolkit().getScreenSize().width - 655) / 2, (Toolkit.getDefaultToolkit().getScreenSize().height - 675) / 2, 655, 675);// 窗口屏幕正中放置 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setResizable(false);//窗体大小固定 jFrame.setVisible(true);//窗体设置可见 NetLTWZQDemo netLTWZQDemo = new NetLTWZQDemo(); jFrame.add(netLTWZQDemo); t = new Thread(netLTWZQDemo); //为了方便(又懒又笨..),直接把所有内容写到一个类里,且把模式选择对话框放在main方法里 int respones = JOptionPane.showConfirmDialog(null, "老铁你愁啥,想找个人怼怼?", "生死看淡,不服就干!", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (respones == JOptionPane.YES_OPTION) { try { UsernameOrIp = JOptionPane.showInputDialog(null, "输入丫主机名或IP", "记小本本上每天看两遍", JOptionPane.WARNING_MESSAGE) .trim(); clientPort = Integer.parseInt(JOptionPane .showInputDialog(null, "老铁从几号门出发?", "门牌号:1024~65535", JOptionPane.WARNING_MESSAGE) .trim()); serverPort = Integer.parseInt(JOptionPane .showInputDialog(null, "对面在几号门?", "门牌号:1024~65535", JOptionPane.WARNING_MESSAGE) .trim()); } catch (Exception e) { JOptionPane.showMessageDialog(null, "输错了,老铁从头再来吧!", "怜悯的眼神看着你", JOptionPane.ERROR_MESSAGE); System.exit(0); e.printStackTrace(); } try { inetAddress = InetAddress.getByName(UsernameOrIp); } catch (UnknownHostException ex) { ex.printStackTrace(); } if (inetAddress != null) { JOptionPane.showMessageDialog(null, "开怼!", "安排上了!", JOptionPane.WARNING_MESSAGE); start = true;//设置游戏开始 if (clientPort == serverPort) {// 若主机名或IP为自己电脑(或不填写)且两个端口一样,则为单机模式 danJi = true;//开启单机模式 } else { danJi = false;// 联机模式,设置单机模式为false } t.start(); } } if (respones == JOptionPane.NO_OPTION || respones == JOptionPane.CLOSED_OPTION) { JOptionPane.showMessageDialog(null, "自怼?啥也别说了,老铁双击666", "冷笑不语", JOptionPane.INFORMATION_MESSAGE); try { inetAddress = InetAddress.getLocalHost(); serverPort = clientPort = 9653;// 默认端口号为9653 start = true;//设置游戏开始 danJi = true;// 开启单机模式 t.start();//开启线程开启游戏 } catch (UnknownHostException ex) { JOptionPane.showMessageDialog(null, "老铁,本地网络出问题了吧?", "是时候换台电脑了老铁", JOptionPane.ERROR_MESSAGE); System.exit(0); ex.printStackTrace(); } } } @Override public void run() {// 使用线程,端口号一致可进行单机游戏 clearChess();//游戏开启,重绘棋盘 receive();//开启服务端接收数据 } public NetLTWZQDemo() { setBackground(Color.gray); addMouseListener(this);//注册鼠标事件 setVisible(true);//设置窗体可见 } public void paintComponent(Graphics g) { Graphics2D g2D = (Graphics2D) g;// 使用Graphics2D使构图平滑 g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); super.paintComponent(g); drawChessBoard(g); drawChess(g); } public void drawChess(Graphics g) { for (int i = 0; i if (chess[i][j] == 1) { g.setColor(Color.black); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } else if (chess[i][j] == 2) { g.setColor(Color.white); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } else if (chess[i][j] == 11) {// 点击落黑子时外加个黑框 g.setColor(Color.black); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); g.drawRect((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } else if (chess[i][j] == 22) {// 点击落白子时棋子外加个白框 g.setColor(Color.white); g.fillOval((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); g.drawRect((i + 1) * 50 - 19, (j + 1) * 50 - 19, 38, 38); } } } } public void drawChessBoard(Graphics g) { // 画棋盘 for (int i = 50; i // 初始化棋盘 for (int i = 0; i chess[i][j] = 0; } } me = true;// 初始化为己方落黑子 repaint(); } //这个判断胜负的方法是抄的网上一段代码,略作修改(为保持原貌几乎没有修改,提醒自己不能这么干,嗯!),emmm,互相学习嘛,哈哈哈(尬笑),实在是想的脑阔疼,偷个懒...(不过好像点的太快,会出现bug,导致没有5子连着也会判胜,可能是错觉吧...) void checkWiner() {// 判断胜方 int black_count = 0; int white_count = 0; for (int i = 0; i if (chess[i][j] == 1 || chess[i][j] == 11) { black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋胜利"); clearChess(); return; } } else { black_count = 0; } if (chess[i][j] == 2 || chess[i][j] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋胜利"); clearChess(); return; } } else { white_count = 0; } } } for (int i = 0; i if (chess[j][i] == 1 || chess[j][i] == 11) { black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋胜利"); clearChess(); return; } } else { black_count = 0; } if (chess[j][i] == 2 || chess[j][i] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋胜利"); clearChess(); return; } } else { white_count = 0; } } } for (int i = 0; i for (int k = 0; k black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋胜利"); clearChess(); return; } } else { black_count = 0; } if (chess[i + k][j + k] == 2 || chess[i + k][j + k] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋胜利"); clearChess(); return; } } else { white_count = 0; } } } } for (int i = 4; i for (int k = 0; k black_count++; if (black_count == 5) { JOptionPane.showMessageDialog(this, "黑棋胜利"); clearChess(); return; } } else { black_count = 0; } if (chess[i - k][j + k] == 2 || chess[i - k][j + k] == 22) { white_count++; if (white_count == 5) { JOptionPane.showMessageDialog(this, "白棋胜利"); clearChess(); return; } } else { white_count = 0; } } } } } @Override public void mouseReleased(MouseEvent e) { System.out.println(e.getX() + "==" + e.getY());// 在棋盘上点击鼠标时在控制台输出坐标信息,方便测试 if (start == false) { return; } ex = e.getX(); ey = e.getY(); if (ex >= 25 && ex = 25 && ey // 当点击位置已经有棋子,则此次点击无效,并继续落子 return; } if (one == true) {// 轮到一方落子时,落子判断胜负,并传送鼠标点击的坐标 doit();// 若传送坐标前判断胜负,则会导致最后一颗棋子落子信息阻塞,判断完毕后再次传送,故此处不判断 sendXY(ex, ey);// 先传送坐标再判断胜负 if (danJi == false) {// 联机模式下才进行此次胜负判断 checkWiner(); } } one = false;// 换到对方落子 } private void doit() {// 判断落子颜色及判断胜方 x = (ex - 19) / 50; y = (ey - 19) / 50; if (chess[x][y] == 0) {// 点击位置无棋子 if (me == true) { chess[x][y] = 11;// 黑子 for (int i = 0; i if (chess[i][j] == 22) { chess[i][j] = 2;// 把刚才下的加白框的白子转换为普通白子 } } } me = false;// 换为白子落子 } else if (me == false) { chess[x][y] = 22;// 白子 for (int i = 0; i if (chess[i][j] == 11) { chess[i][j] = 1;// 把刚才下的加黑框的黑子转换为普通黑子 } } } me = true;// 换为黑子落子 } } repaint();// 重绘棋盘后判断胜方 } public void sendXY(int xSend, int ySend) {// 传送鼠标点击的坐标 try {// 问题在于每次传送坐标都要重新创建一个socket对象,不知道怎么改进 Socket socket = new Socket(inetAddress, serverPort);// 设置对方服务器端IP,并设置端口号为9653; OutputStream os = socket.getOutputStream(); String strr = xSend + "-" + ySend;// 使用-符号连接坐标,并以字符串形式发送到服务端 os.write(strr.getBytes()); socket.shutdownOutput();// 及时发送位置信息,不用closs()是保持通信一直连接 } catch (Exception ex) { JOptionPane.showMessageDialog(null, "老铁,多人一起怼不合适吧?", "怼亦有道!", JOptionPane.ERROR_MESSAGE); System.exit(0); ex.printStackTrace(); } } public void receive() {// 服务端接收对方发来的坐标 try { ServerSocket serverSocket = new ServerSocket(clientPort);// 从该端口接收数据 while (true) { Socket sSocket = serverSocket.accept(); InputStream is = sSocket.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String strReceive = new String(bys, 0, len); System.out.println(strReceive);//接收到的字符串输出在控制台,方便测试 String[] strs = strReceive.split("-");// 使用"-"分割字符串,得到坐标 ex = Integer.parseInt(strs[0]); ey = Integer.parseInt(strs[1]); doit();// 得到坐标后,重绘棋盘并判断胜方 checkWiner(); one = true;// 己方不能落子,棋权交给对方 } } catch (IOException e) { JOptionPane.showMessageDialog(null, "老铁,多人一起怼不合适吧?", "怼亦有道!", JOptionPane.ERROR_MESSAGE); System.exit(0); e.printStackTrace(); } } @Override public void mouseClicked(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有