第一次写博客,排版啥的都比较乱,大家不要嫌弃啊。
所谓暴力开发,其实是啥也不会,硬怼哈哈哈。只是刚学会一点JAVA编程,参考网络资源写了个联机版五子棋(支持单机),来这里记录一下学习过程,而且我也是靠别人的分享才能写出来,所以本着互相帮助的原则,分享给大家,抛砖引玉,共同进步。
作为一个半路出家,正在学习道路上的渣渣,摸索着写了这样一个乱乱的五子棋,很多地方都不正规,比如因为没有系统学过GUI,对话框都用的JOptionPane组件,模式选择对话框也放在main方法里,算是偷懒吧。。
正如标题里写的“一个类一个main”,写的时候为了快速且方便,都放在一个类里了,因此大家想要测试的话,直接整个复制就行了,没啥黑科技,JDK自带的包就能运行。
因为个人的恶趣味,本五子棋起名“老铁互怼五子棋”,当然各位可以自己随意修改。
转载也请随意,标明出处就行了(如果感觉有用,点个赞就是最好的支持啦)。
游戏打开后是这个界面,直接选择游戏模式,不能半途选别的模式,如果要更换,需要重启游戏(不然老出线程异常,所以直接砍掉这个功能哈哈哈。。。)。
选择“是”则为联机版,“否”或关闭对话框为单机版。
联机版需要输入对方的主机名或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 < 12; i++) {
for (int j = 0; j < 12; j++) {
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 <= 600; i += 50) {
g.setColor(Color.WHITE);
g.drawLine(i, 50, i, 600);
g.drawLine(50, i, 600, i);
}
}
public void clearChess() {// 初始化棋盘
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
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 < 12; i++) {// 横向判断
for (int j = 0; j < 12; j++) {
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 < 12; i++) {// 竖向判断
for (int j = 0; j < 12; j++) {
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 < 7; i++) {// 左向右斜判断
for (int j = 0; j < 7; j++) {
for (int k = 0; k < 5; k++) {
if (chess[i + k][j + k] == 1 || chess[i + k][j + k] == 11) {
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 < 12; i++) {// 右向左斜判断 11->12
for (int j = 6; j >= 0; j--) {
for (int k = 0; k < 5; k++) {
if (chess[i - k][j + k] == 1 || chess[i - k][j + k] == 11) {
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 <= 625 && ey >= 25 && ey <= 625) {// 控制鼠标点击位于棋盘内部
x = (ex - 19) / 50;// 使棋子排列在棋盘交点处
y = (ey - 19) / 50;
}
if (chess[x][y] != 0) {// 当点击位置已经有棋子,则此次点击无效,并继续落子
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 < 12; i++) {
for (int j = 0; j < 12; j++) {
if (chess[i][j] == 22) {
chess[i][j] = 2;// 把刚才下的加白框的白子转换为普通白子
}
}
}
me = false;// 换为白子落子
} else if (me == false) {
chess[x][y] = 22;// 白子
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
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) {
}
}
更多推荐
JAVA联机版五子棋——源码(一个类一个main暴力开发)
发布评论