免责声明:对不起,如果我的回答在最后开始变得草率。
另外,我在底部有一个代码,显示了我在行动中谈到的所有内容。
我认为我能说的最简单的事情就是使用更多的方法和可能的类。首先,避免所有代码重复的方法之一是使用面向对象编程来编写它们。这是让多个类都与主类交互以帮助编写代码的想法。我不会在这里谈论这个,但如果你有兴趣让你的代码整洁和“干净”,我强烈建议你去看看。此外,还有一本关于该主题的好书,名为Clean Code by Robert C. Martin。我将简单地展示如何利用方法来缩短和清理代码。你重复最多的一件事就是这个
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
你的 countX 和 countO 每次都不一样,所以你重写了。我更简单、更有效的方法是使用方法。我建议您研究 Java 的语法,因为您不知道如何创建方法或类,但您确实使用了 determineWinner() 方法的语法,所以我假设您理解它。您可以使函数具有本质上是可以在整个函数中访问和修改的输入的参数。 (顺便说一句,您不能在 Java 中的方法内部创建方法,因此您需要将下一个方法放在类的其他位置之外。)
public String checkCounts() {
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
else return "N/A";
}
*您想在使用带有 if 语句的方法时检查它是否返回“N/A”。如果是这样,你应该忽略它,因为没有人赢。
whoWon = checkCounts();
//In the code I put at the bottom I will make whoWon a global variable, which is why I'm not defining it here.
//It will be already defined at the top of the code.
if (!whoWon.equals("N/A")) return whoWon;
*该!符号表示不,a.k.a 如果 whoWon 不等于“N/A”,则返回 whoWon。
这样,当您需要写出 if 语句代码时,您只需编写 checkCounts 并插入您刚刚从数组中获得的两个变量。你会写 checkCounts();在这种情况下。现在,如果您只是说 return checkCounts();然后代码将运行所有这些 if 语句,而无需您全部键入并返回结果。您实际上也重复了很多其他事情。这几行
String value = this.field[x][y];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
和这几行很相似
String value = this.field[i][i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
还有这几行
String value = this.field[i][2-i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
因此您可以将它们全部浓缩为一种具有三种不同输入的方法。该方法将返回 0、1 或 2。目标是检查它使用给定的字符串输入返回哪一个,然后将其转换为要添加 1 的变量。
如果是0,忽略,如果是1,countX++,如果是2,countY++。
public int checkString(String value) {
int whichCount = 0;
//if whichCount is 1, it means X
//if whichCount is 2, it means O
if (value.equals("X")) {
whichCount = 1;
}
if (value.equals("O")) {
whichCount = 2;
}
return whichCount;
}
Switch 语句可能有点高级,但它们的概念非常简单。它是一堆 if 语句,语法非常方便。括号内的值是您的输入,或要检查的内容。案例说,当它等于这个时,这样做。当您需要在 for 循环中增加 countX 或 countY 时,您可以编写
switch (checkString(this.field[coord1][coord2])) {
case 1 -> countX++;
case 2 -> countO++;
}
案例 1 表示,如果 addToCount() 返回 1,则执行箭头右侧的操作,案例 2 表示如果它返回 2 到该箭头右侧的操作。在您的 for 循环中,coord1 和 coord2 可以是从 [x][y] 到 [i][i] 到 [i][2-i] 的任何内容,因此您可以在任何时候更改 switch 语句。
此外,您可以将 switch 语句本身转换为方法。
public void adjustCounts(String stringFromArray) {
switch (checkString(stringFromArray)) {
case 1 -> countX++;
case 2 -> countO++;
}
}
您还可以通过缩短 if 语句来减少几行。如果 if 语句中的内容只有一行长,则可以放在它旁边。
if (bool) {
doSomething();
}
//Change that to this
if (bool) doSomething();
你经常重复的另一件事是这个
countX = 0;
countO = 0;
我刚刚做了一个非常简单的方法,没有参数。
public void resetCounts() {
countX = 0;
countO = 0;
}
这几乎是重复的内容,但我认为您的 determineWinner 方法仍然太大。即使您不再重复任何代码,对它进行大量更改并将其分成更小的部分也可以使其更易于阅读和理解。
我添加了一堆只包含你的 for 循环的方法。他们将在我提出的最后一堂课的最底层。它有 85 行长,所以从技术上讲,它只改进了 4 行,但它更简洁。此外,如果您要将它嵌入到您的实际类中,而不仅仅是在一个方法中(因为您不能将它们全部放在一个方法中),那么它会更加有效,因为您可以访问所有类全局变量。这是我想出的代码,但我强烈建议您对面向对象编程进行额外研究,以真正改进您的代码。
public class TicTacToe {
String[][] field = new String[3][3];
int countX, countO = 0; // amount of X's and O's in a row
String whoWon = "N/A";
public int getNumberOfMoves() {return 0;} //Whatever you method did that determined this. Obviously it didn't really just return 0.
public String determineWinner() {
String columns = checkColumnsForWinner();
String rows = checkRowsForWinner();
String diagonal1 = checkDiagonal(1, 0);
String diagonal2 = checkDiagonal(-1, 2);
if (checkForNA(columns)) return columns;
if (checkForNA(rows)) return rows;
if (checkForNA(diagonal1)) return diagonal1;
if (checkForNA(diagonal2)) return diagonal2;
if (this.getNumberOfMoves() == 9) return "draw"; // if the number of moves equals 9, the game is over and it is a draw
return "game not finished";
}
public String checkCounts(int countX, int countO) {
if (countX == 3) return "X wins";
if (countO == 3) return "O wins";
else return "N/A";
}
public int checkString(String value) {
int whichCount = 0;
//if whichCount is 1, it means X
//if whichCount is 2, it means O
if (value.equals("X")) whichCount = 1;
if (value.equals("O")) whichCount = 2;
return whichCount;
}
public void adjustCounts(String stringFromArray) {
switch (checkString(stringFromArray)) {
case 1 -> countX++;
case 2 -> countO++;
}
}
public void resetCounts() {
countX = 0;
countO = 0;
}
public String checkRowsForWinner() {
for (int y = 0; y <= 2; y++) { // for all horizontal rows
resetCounts();
for (int x = 0; x <= 2; x++) { // loop through all x-coordinates
adjustCounts(field[x][y]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
}
return "N/A";
}
public String checkColumnsForWinner() {
for (int x = 0; x <= 2; x++) {
resetCounts();
for (int y = 0; y <= 2; y++) {
adjustCounts(field[x][y]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
}
return "N/A";
}
public String checkDiagonal(int mutiply, int add) {
resetCounts();
for (int i = 0; i <= 2; i++) {
adjustCounts(field[i][i*mutiply + add]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
return "N/A";
}
public boolean checkForNA(String string) {return !string.equals("N/A");}
}
关于面向对象的编程,我能看到你在这个例子中实践的最好的例子是抽象。这是一个非常笼统的概念,但我认为在这种情况下会有很大帮助。在我上面的程序中,我有一个井字游戏类,以及我所有的代码。问题是,您会看到很多样板文件来运行代码。最大的例子是您拥有的 2D Array 对象。您必须做很多事情才能从中获得 X 或 O。建立一个新课程(可能称为 Board)会更好(意见)。它将包含一个私有 2D Array 对象和从该对象获取值的公共方法。此外,(这实际上只是我的观点)我建议您使用枚举而不是字符串作为数组值。例如
public enum BoardValues {
X,
O,
EMPTY
}
然后您可以创建一个类来将这些棋盘值放置在一个 3x3 网格中。
public class Board {
private BoardValues[][] values = new BoardValues[3][3];
public BoardValues getValue(int x, int y) {
return values[x][y];
}
public BoardValues[] getRow(int rowNumber) {
BoardValues[] rowValues = new BoardValues[3];
for (int i = 0; i < values.length; i++) {
rowValues[i] = getValue(i, rowNumber);
}
return rowValues;
}
public BoardValues[] getColumn(int columnNumber) {
BoardValues[] columnValues = new BoardValues[3];
for (int i = 0; i < values.length; i++) {
columnValues[i] = getValue(columnNumber, i);
}
return columnValues;
}
public void setValues(BoardValues[][] values) {
this.values = values;
}
public void setValue(int x, int y, BoardValues value) {
values[x][y] = value;
}
}
现在,您无需使用那个讨厌的旧 2D 数组,您只需创建一个板对象并在需要时随意设置和获取它的值。此外,我没有添加对角线,但你仍然可以很容易地,我的只是为了证明概念。这是抽象,可能是 OOP 概念中最容易掌握的,因为它太笼统了。我只是在掩盖您在尝试编写游戏代码时不需要看到的信息。