最近在学习Java中的IO流时,拿着以前写的五子棋来练手,就是实现五子棋的游戏存档和存档读取。经过几次修改代码后,忽然对代码的编写之道有了一些自己的看法。
首先介绍一下我的界面:
这是一张15*15的五子棋标准棋盘,我找了一张棋盘的照片,然后用ps测出他的颜色为:
new Color(241,193,121)
还有一些基础数据为:
private int length_i = 15;//此为行数
private int length_j = 15;//此为列数
private int edge_length = 30;//此为边界长度
private int cell_length = 40;//此为单元格长度
private int[][] chessArray = new int[length_i][length_j];//存储棋子的数组
对了,我新建了一个JPanel对象来当棋子的画板,并把它加到界面上作为中间面板,这样就可以避免界面上这个框对我们实际得到的x坐标,y坐标的影响。(new Dimension(780, 680):这是界面的大小)
GoListener gl=new GoListener(chessArray,this,edge_length,cell_length);
this.addMouseListener(gl);
为我们的画板(即中间面板)加上鼠标监听器。
public class GoListener implements MouseListener,ActionListener{
private int x,y,i,j,count=1;//count为棋子次序
private int edge_length,cell_length;
private Graphics g;
private int[][]array;//0是空,大于0的,奇数为黑子,偶数为白子
private GoBang frame;
private boolean flag=false;//是否有胜利者的标识符
/**
*
* @param array
* @param frame
* @param edge_length 边界长度
* @param cell_length 单元格长度
*/
public GoListener(int[][] array,GoBang frame, int edge_length, int cell_length){
this.array=array;
this.frame=frame;
this.edge_length=edge_length;
this.cell_length=cell_length;
}
public void setGraphics(Graphics g){
this.g=g;
}
妥妥的,我们的监听器类,大家可以看到它还继承操作监听器,所以我把传递画笔的代码写到了构造方法之外。有人可能会问,为什么不让使用这几行代码来获得画笔g?
public GoListener(int[][] array,GoBang frame, int edge_length, int cell_length){
this.array=array;
this.frame=frame;
this.edge_length=edge_length;
this.cell_length=cell_length;
this.g=frame.getGraphics();
}
哈哈,眼尖的读者肯定看到了,我们传递的是整个界面:GoBang frame;如果写成上面那样,我们就还要传递个中间面板过来,然后再在其上获取画笔,这岂不是多此一举?
public void mouseClicked(MouseEvent e) {
// 将g强制转换为Graphics2D类型的对象
Graphics2D g2d = (Graphics2D) g;
if(flag==false){
//将棋子下在交叉点
x = e.getX();y = e.getY();//获得鼠标点击点的x坐标和y坐标
for (j = 0; j < 15; j++) {//行
b = 30 + 40 * j;
if (Math.abs(x - b) < 25)
break;
}
for (i = 0; i < 15; i++) {//列
a = 30 + 40 * i;
if (Math.abs(y - a) < 25)
break;
}
x = b;y = a;
if(i>=0&&i<=14&&j>=0&&j<=14){//棋子需下在棋盘内
if(array[i][j]==0){//只有下在空的交叉点上
if(count%2==1){
//black棋
g2d.setColor(new Color(65,52,43));
array[i][j]=count;//记录棋子的次序
count++;//次序加1
}else{
//white棋
g2d.setColor(new Color(248,248,238));
array[i][j]=count;
count++;
}
g2d.fillOval(x - 20, y - 20, 40, 40);
//Judge为判定输赢得类,需要传入存储数据的数组
Judge judge=new Judge(array);
flag=judge.isWin(i,j);
//Judge类中的isWin()方法,需要传入所下棋子的行数和列数,如果有胜利者则返回true;没有则返回false;
if(flag){
if(array[i][j]%2==1)//次序为奇数,下的是黑棋
JOptionPane.showMessageDialog(null, "黑棋赢了!");
if(array[i][j]%2==0)//次序为非零偶数,下的是白棋
JOptionPane.showMessageDialog(null, "白棋赢了!");
}//判定输赢
}//如果交叉点上有棋子,就什么事都不做。
}//如果鼠标点击点不在棋盘内,就什么事也不做。
}else{
int str=JOptionPane.showConfirmDialog(null,"是否开始新游戏" );
if(str==0)
Restart();
}
}
这里我用了两个for循环来获得棋子所想要的行数 i 和列数 j ,然后将count存在array[i][j]中,count即正在下的棋子的次序,画完棋子后,count++。
好的,于是我们的第一个变革点来了
public void mouseClicked(MouseEvent e) {
// 将g强制转换为Graphics2D类型的对象
Graphics2D g2d = (Graphics2D) g;
if(flag==false){
x = e.getX();y = e.getY();//获得鼠标点击点的x坐标和y坐标
//除法公式
i = (y-edge_length+cell_length/2)/cell_length;
j = (x-edge_length+cell_length/2)/cell_length;
//将棋子下在交叉点
x = edge_length+j*cell_length;y = edge_length+i*cell_length;
if(i>=0&&i<=14&&j>=0&&j<=14){
if(array[i][j]==0){
System.out.println(i+">>>"+j);
if(count%2==1){
//black
g2d.setColor(new Color(65,52,43));
array[i][j]=count;
count++;
}else{
//white
g2d.setColor(new Color(248,248,238));
array[i][j]=count;
count++;
}
g2d.fillOval(x - cell_length/2, y - cell_length/2, cell_length, cell_length);
//判断棋子输赢
Judge judge=new Judge(array);
flag=judge.isWin(i,j);
if(flag){
if(array[i][j]%2==1)
JOptionPane.showMessageDialog(null, "黑棋赢了!");
if(array[i][j]%2==0)
JOptionPane.showMessageDialog(null, "白棋赢了!");
}//判定输赢
}//如果交叉点上有棋子,就什么事都不做。
}//如果鼠标点击点不在棋盘内,就什么事也不做。
}else{
int str=JOptionPane.showConfirmDialog(null,"是否开始新游戏" );
if(str==0)
Restart();
}
}
是的,我们改变了判定行数和列数的代码,之前的代码的时间复杂度为O(n),现在的时间复杂度为O(1)。
小小的一个改变,收益巨大,也许你现在感觉不到什么,但是以后你写大型程序的时候,你就会知道代码所需要的高效性有多重要了。
我们接着往下,接着就是我们的游戏存档了:
new ArrayToFile(array);//传入存储数据的数组
简简单单的一行代码,干完我们所需要的所有事情,这是大家在团队合作中的最好的工作结果,别人只要创建对象,就能做完交给你的所有事情(我交给ArrayTOFile类的事情就是让它把数组存成文件)。
继续当然就是存档读取:
public class FileToArray {
private int[][] array=new int[15][15];
private InputStream ins;
private DataInputStream read_file;
private int i,j;
//此行代码是为了能够一直读下去,而不是从头开始。
public FileToArray(){
try{
/**检测D盘是否存在GoBang这个文件。*/
File filefound=new File("D://Go.tx");
if(filefound.exists()){
ins=new FileInputStream(filefound);
read_file=new DataInputStream(ins);
FileRead();
System.out.println(">>>");
}
else{
JOptionPane.showMessageDialog(null, "没有存档!请存档。");
}
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
/**
* 构造函数
* @throws IOException
*/
public void FileRead() throws IOException{
while((i=read_file.readInt())!=99 ){
j=read_file.readInt();
array[i][j]=read_file.readInt();
System.out.println(i+" "+j+" "+array[i][j]);
}
System.out.println("输出完毕!\n\n\n");
}
public int[][] getArray() {
return array;
}
}
可以看到,我在这个类中new了一个15*15的数组对象来存储从文件中读取的数据。然后用一个getArray()方法来返回这个数组。
调用出的代码如下:
array=new FileToArray().getArray();
count=getMax(array)+1;
//判断棋子输赢
Judge judge=new Judge(array);
judge.isWin(getMax_i(array),getMax_j(array));
frame.setChessArray(array);
//将数组设置回去。其余的不用设置是因为他们在内存中的地址和从GoBang传来的数组一样,而这里改变了数组array的地址。
这是我的paint(Graphics g)方法:
public void paint(Graphics g){
super.paint(g);
// 将g强制转换为Graphics2D类型的对象
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(new Color(190,145,103));
// 棋盘的起始坐标是30,30;横线和竖线各15条;格子的宽度高度是40。
for (int i = 0; i < 15; i++) {
if (i == 0 || i == 14)
g2d.setStroke(new BasicStroke(5));// 设置线条的粗细
else
g2d.setStroke(new BasicStroke(2));// 设置线条的粗细
// 绘制横线和竖线
g2d.drawLine(edge_length, edge_length + cell_length * i, edge_length + 14 * cell_length, edge_length + cell_length * i);
g2d.drawLine(edge_length + cell_length * i, edge_length, edge_length + cell_length * i, edge_length + 14 * cell_length);
}
// 绘制矩形
g2d.setStroke(new java.awt.BasicStroke(4));
g2d.drawRect(edge_length, edge_length, cell_length*14, cell_length*14);
g2d.fillRect(145, 145, 10, 10);
g2d.fillRect(465, 145, 10, 10);
g2d.fillRect(465, 465, 10, 10);
g2d.fillRect(145, 465, 10, 10);
g2d.fillRect(305, 305, 10, 10);
// 把下过的棋子重绘一次
for(int i=0;i<chessArray.length;i++){
for(int j=0;j<chessArray[i].length;j++){
if(chessArray[i][j]!=0){
if(chessArray[i][j]%2==1){
g.setColor(new Color(65,52,43));
}else{
g.setColor(new Color(248,248,238));
}
g2d.fillOval(10+cell_length*j, 10+cell_length*i, cell_length, cell_length);
}
}
}
}
大家按照上面的判定标准肯定可以看出我这个FileToArray类写的很水,为什么呢?因为别人(GoListener对象调用我还需要让GoBang类增加一个setArray(int[][] array)方法(如果没有此函数,那么重绘时就没有我们从文件中读取的数组的事情了),如此兴师动众。
于是我将代码改成了这样:
public class FileToArray {
private Reader read_file;
private int i,j;
//此行代码是为了能够一直读下去,而不是从头开始。
public FileToArray(int[][] array){
Array_clear(array);
try{
/**检测D盘是否存在GoBang这个文件。*/
File filefound=new File("D://Go.tx");
if(filefound.exists()){
read_file=new FileReader(filefound);
FileRead(array);
}
else{
JOptionPane.showMessageDialog(null, "没有存档!请存档。");
}
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
/**
* 构造函数
* @param array
* @throws IOException
*/
public void FileRead(int[][] array) throws IOException{
while((i=read_file.read())!=99 ){
j=read_file.read();
array[i][j]=read_file.read();
System.out.println(i+" "+j+" "+array[i][j]);
}
System.out.println("输出完毕!\n\n\n");
}
/**
* 清空一个数组
*/
public void Array_clear(int[][] array){
for(int i=0;i<array.length;i++){
for (int j=0;j<array[i].length;j++){
array[i][j]=0;
}
}
}
}
我传入了存储数据的数组,哈哈,如此,我们在这边对数组做的改变,GoBang类里就可以直接感受到了(
因为是传址的)。
那样调用处的代码就改成了这样:
new FileToArray(array);//这里用的是传址发
count=getMax(array)+1;
//判断棋子输赢
Judge judge=new Judge(array);
judge.isWin(getMax_i(array),getMax_j(array));
frame.repaint();
good!现在我们完成任务同样只需要创建一个对象了。
而且现在的FileToArray类不仅仅只能读取15*15的棋子数组了,它能读取任何行数和任何列数的二维数组。
我将我的ArrayToFile类和FileToArray类复制到我的2048游戏里,同样只通过创建一个对象,就完成了我的2048的游戏存档和存档读取功能(因为我的2048数据也是用数组来存储数据的)。
从这几次修改代码我们可以明白,作为一个程序员,我们写的代码不能只成为一次性的消费品,我们应该写能够在其他程序中也能使用的代码,还应该对我们写的代码的时间复杂度和空间复杂度负责,并且我们写的代码应该要做好我们接到的任务中的所有事情,避免对其他类的代码修改需要。
这就是我从我这几次修改我的代码中获得的总结,谢谢大家的阅读!也欢迎大家提出自己的意见!我们一起交流进步,成为最强大的程序员!
我把我的代码打包贴了上来,大家可以下载去看。
- 大小: 77.8 KB
- 大小: 20.3 KB
分享到:
相关推荐
这种实践方式对于编程初学者来说是非常有价值的,因为直接看成品代码比理论学习更能让人们理解编程逻辑。 “希望对你能有帮助”表明这个资源是一个教学性质的共享,旨在帮助那些想要学习VB编程或增强其GUI应用开发...
从压缩包子文件的文件名来看: 1. "jldx.bat" - 这可能是一个批处理文件,通常用于自动化执行一系列DOS命令。在VB项目中,它可能是用于编译或运行程序的辅助脚本。 2. "Form2.frm" 和 "Form5.frm" - 这些是VB中的...
此外,从压缩包中的文件名“css3-text-water-loading”来看,我们可以推测“代码管家”可能包含了一些关于CSS3的技术,特别是针对文本的水印加载效果。CSS3是层叠样式表的最新版本,提供了许多新的选择器、动画和...
首先,我们来看一下Verilog代码编写规范的主要目标: 1. **缩小篇幅提高整洁度**:简洁的代码更容易跟踪和分析,从而降低调试和修改的难度。 2. **增强可读性**:清晰的命名和结构化的代码帮助阅读者快速理解功能和...
总的来说,这样的JS代码编写工具是提升开发效率、减少错误的关键。它通过智能提示、代码高亮、错误检查等功能,为JavaScript程序员创造了一个友好、高效的编程环境。无论是初学者还是经验丰富的开发者,都能从中受益...
接着,我们来看源代码的核心部分,即Activity的Java代码。这个Activity通常继承自`AppCompatActivity`,并实现与用户交互的逻辑。源代码中的Activity可能命名为`CalculatorActivity.java`。在这个类中,你需要找到对...
从标签来看,“计算器”表示这是个进行数学计算的软件,“源代码”意味着我们能查看并修改程序的内部逻辑,“MFC”代表它使用的开发框架,“VC6”则明确了开发工具。 压缩包内的文件名列表提供了关于项目的一些具体...
2. **MP3播放器**:虽然从实际代码来看,这个程序并不是一个完整的MP3播放器,但标签暗示了开发者的目标可能是创建一个能够播放MP3格式音频文件的程序。 #### 源代码分析 1. **导入模块**:首先引入了一系列标准库...
通过研究和修改代码,你可以提升对HTML语义的理解,提高CSS布局和美化的能力,以及增强JavaScript动态效果的编写技巧。 总的来说,"爱心网页-HTML-代码"是一个优秀的学习资源,它不仅展示了HTML、CSS和JavaScript的...
从提供的文件名"Game"来看,这可能是一个包含整个游戏项目的文件夹或者是一个主程序文件。在C#游戏开发中,"Game"通常表示游戏的主类,它会包含游戏的初始化、更新循环、渲染以及与其他游戏组件的交互等关键功能。...
通过石器代码查询器,玩家和开发者可以查看并理解这些代码,进一步分析游戏的运行机制,甚至对游戏进行修改和定制。 石器代码查询器作为一款专用工具,它的主要功能在于帮助用户查阅和分析游戏中的代码。这个工具...
良好的代码格式不仅使代码看起来更专业,而且有助于团队协作,因为大家都能按照一致的样式阅读和修改代码。 `AStyle`(Artistic Style)是一款开源的代码格式化工具,它支持C、C++、C++11、Objective-C、C#和Java等...
首先,让我们来看看标签"程序员的代码的代码的代码"。这可能是对程序员编写高质量、可维护和可扩展的代码的挑战的一种象征。在实际开发中,代码通常需要具备良好的结构和清晰的逻辑,以便其他开发者理解和修改。因此...
首先,我们来看看MATLAB的基本编程概念。MATLAB的编程基于向量和矩阵运算,这使得处理大量数据变得非常方便。源代码中可能会涵盖如何创建、修改和操作矩阵,以及如何利用循环、条件语句和函数进行控制流程。理解这些...
例如,想要自动调整图表格式时,你需要知道图表对象有哪些属性(如标题、数据源)和方法(如SetSourceData),以及如何通过代码来访问和修改它们。 ### 结论 VBA作为Excel的自动化利器,其强大之处在于能够通过...
根据标题《你的代码很烂,我们一起来修改》和描述“一起来学习一下 看起来很烂的代码 怎么修改的让人赏心悦目”,我们可以推断这将是一个关于代码重构、代码质量提升和编写优雅代码的教程。以下内容将详细介绍如何...
首先,我们来看“职业代码”。在冒险岛游戏中,每个职业都有其特定的标识代码,这些代码用于区分不同的职业并进行相关的属性计算和技能处理。例如,“职业代码.txt”文件中可能包含了战士、弓箭手、法师等各类职业的...
它基于Windows操作系统,并且是开源的,这意味着它的源代码是公开的,任何人都可以查看、修改和改进。这款软件支持多种编程语言,包括但不限于C、C++、Java、Python、PHP、HTML、XML等,为程序员提供了极大的便利。 ...
垃圾代码生成器在这种情况下派上了用场,它能够自动生成大量代码,这些代码可能包含无意义的函数、变量和类,甚至可以模拟一些基本的UI交互,使应用看起来具有一定的功能性,但实际可能并无实质性用途。 使用【垃圾...