注:我是一个Java初学者(2-3个月的经验)。
在JetBrains/超级杀手上做一个关于制作Tac脚趾游戏的项目时,我发现自己在试图确定游戏的胜利者时重复了相当多的代码。要将游戏表示为一个坐标系(因此,1,1位于左下角,3,3位于右上),我使用的是一个二维数组。这是决定胜利者的功能:
public String determineWinner() {
int countX = 0; // amount of X's in a row
int countO = 0; // amount of O's in a row
for (int y = 0; y <= 2; y++) { // for all horizontal rows
countX = 0;
countO = 0;
for (int x = 0; x <= 2; x++) { // loop through all x-coordinates
String value = this.field[x][y];
if (value.equals("X")) { // if the value at that coordinate equals "X", add 1 to the count
countX++;
}
if (value.equals("O")) { // same here
countO++;
}
}
if (countX == 3) { // if the count is 3 (thus 3 X's in a row), X has won
return "X wins";
}
if (countO == 3) { // same here
return "O wins";
}
}
// Same thing, but for all vertical columns
for (int x = 0; x <= 2; x++) {
countX = 0;
countO = 0;
for (int y = 0; y <= 2; y++) {
String value = this.field[x][y];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
}
// Same thing, but for diagonal
countX = 0;
countO = 0;
for (int i = 0; i <= 2; i++) {
String value = this.field[i][i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
// Same thing, but for other diagonal
countX = 0;
countO = 0;
for (int i = 0; i <= 2; i++) {
String value = this.field[i][2-i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
if (this.getNumberOfMoves() == 9) { // if the number of moves equals 9, the game is over and it is a draw
return "draw";
}
return "game not finished";
}目前,代码允许您设置一个启动板(所有O和X的启动安排),然后允许您进行1次移动。在此之后,游戏决定谁是赢家或是否是平局等等。
正如人们很快注意到的那样,这个函数太长了,并且有相当多的重复,但是我无法想出任何方法来缩短它。
有人有小费吗?或者任何适用于所有代码的准则?
发布于 2020-05-11 03:45:12
免责声明:对不起,如果我的答案开始草率接近尾声。此外,我在底部有一个代码,显示了我在行动中谈到的所有事情。
我认为我能说的最简单的事情是使用更多的方法,可能还有类。首先,避免代码重复的方法之一是使用面向对象的编程来编写代码。这就是让多个类都与主类交互以帮助编写代码的想法。我不会在这里讨论这个问题,但是如果您对使代码整洁和“干净”感兴趣,我强烈建议您查找一下。此外,还有一本关于这个主题的很棒的书,叫做清洁代码罗伯特C马丁。我将简单地向您展示如何利用方法来缩短代码和清理代码。你重复最多的一件事是
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;*!如果whoWon不等于"N/A",则返回whoWon。
这样,只要您需要写出if语句代码,就可以编写checkCounts并插入刚刚从数组中获得的两个变量。在本例中,您将编写checkCounts()。现在,如果您只是说返回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。目标是检查它用给定的字符串输入返回哪一个变量,然后将该变量添加到哪个变量中。
如果是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到I到I的任何东西,因此您可以随时更改它。
此外,还可以将开关语句本身转换为方法。
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");}
}关于面向对象的编程,我在这个示例中看到的最好的示例是抽象。这是一个非常普遍的概念,但我认为在这种情况下会有很大帮助。在我上面的程序中,我有一个TicTacToe类,以及它中的所有代码。问题是,为了让代码运行,您看到了大量的样板。最大的例子是您拥有的2D Array对象。你必须做很多事情才能把X或O弄出来。做一个新的班级,也许叫董事会,会好得多。它将包含一个私有的2D Array对象,以及从该对象获取值的公共方法。此外,(这确实是我的观点),我建议为数组值使用枚举而不是String。例如
public enum BoardValues {
X,
O,
EMPTY
}然后,您可以创建一个类,将这些板值放在一个3x3Grid中。
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概念中最容易掌握的,因为它太笼统了。我只是模糊了你不需要看到的信息,当你试图编码你的游戏。
https://stackoverflow.com/questions/61693881
复制相似问题