How to optimize the game Battleship in JAVA?

Asked

Viewed 660 times

12

I don’t know if this is allowed here on the site, so in case I’m breaking any rules let me know!

I recently started learning the JAVA language. I made a naval battle game (human x computer) using everything I have learned so far and would like to hear opinions of more experienced programmers on the game in order to improve my knowledge.

Some points like what would you have done differently? How would you optimize existing code? I made some redundancy or wrote more than necessary?

How I thought the game schedule?

I created two arrays 5x5. One stores the game board. It starts with the default value of 0 for all elements. With one method, I draw three of these elements, which will represent the position of the boats to be sunk. To them I assign the value 1. The other array stores the positions where the player fired, 0 symbolizes an area where it has not yet attacked and 1 an area where it attacked.

To display the board, I cross the two arrays in a check. Where it has not yet attacked, it is listed as "~", where it was attacked and there is no ship as "*" and where it attacked and sank a ship as "X". I created a method so that at each turn a hint is displayed informing if the amount of existing ships in the row and column of the last shot performed.

Code:

Battalanaval.java

package batalhanaval;
import java.util.Scanner;

public class BatalhaNaval
{
    public static void main(String[] args)
    {
        Scanner entrada = new Scanner(System.in);
        Tabuleiro tab = new Tabuleiro();
        char option; //Opção do menu principal

        do
        {
            showMenu();
            option = entrada.next().charAt(0);
            System.out.println("\n\n");
            switch(option)
            {
                case '1': //Novo jogo
                {
                    initGame(entrada, tab);
                    break;
                }
                case '2':
                {
                    System.out.println("O seu objetivo no jogo é afundar os 3 navios inimigos que encontram-se no seu litoral.\n"
                            + "Para fazer um disparo, informe a posição(linha e coluna) do seu mapa na qual deseja lançar o míssel.\n"
                            + "O símbolo ~ indica uma área ainda não conhecida.\n"
                            + "O símbolo * indica uma área na qual você disparou e não havia nenhum navio.\n"
                            + "O símbolo X indica uma área onde você afundou um navio inimigo.\n"
                            + "A cada disparo realizado você receberá uma dica informando se há navios na linha e na coluna nas quais você realizou o disparo.");
                    break;
                }
                case '3': //Recorde
                {
                    if(tab.recorde == 1000)
                    {
                       System.out.println("<Recorde atual: nenhum>");
                    }
                    else
                    {
                       System.out.println("<Recorde atual: " + tab.recorde + " turnos>");
                    }
                    break;
                }
                case '4':
                {
                    System.out.println("Finalizando...");
                    break;
                }
                default:
                {
                    System.out.println("Opção inválida!");
                }
            }
            System.out.println("\n");         
        }while(option != '4');

    }
    //Método que retorna true caso a String seja um número inteiro
    public static boolean isInt(String str)
    {
        try 
        {  
            Integer.parseInt(str.trim());
            return true;     
        } 
        catch (NumberFormatException ex) 
        {  
            return false;         
        }  
    }
    //Main menu
    public static void showMenu()
    {
        System.out.println("Bem vindo ao jogo <Batalha Naval>!!! (Criado por Talendar)\nEscolha uma opção:");
        System.out.println("1 - Novo jogo");
        System.out.println("2 - Tutorial");
        System.out.println("3 - Recorde");
        System.out.println("4 - Sair");
    }
    //Jogo
    public static void initGame(Scanner entrada, Tabuleiro tab)
    {
        String linha = ""; //As variáveis linha e coluna foram criadas como String para poderem ser submetidas a checagem do método isInt(str) localizado abaixo.
        String coluna = "";

        tab.initTab();
        tab.initShips();
        do
        {
            System.out.println("\n");
            //Exibe a dica a partir do segundo turno
            if(tab.turno > 1)
            {
                tab.dica(Integer.parseInt(linha), Integer.parseInt(coluna));
            }
            System.out.println();

            //Exibe o tabuleiro em seu estado atual
            tab.printTab();

            //Pede a linha
            System.out.print("Linha:");
            linha = entrada.next();
            while(!isInt(linha))
            {
                System.out.println("Apenas números inteiros de 1 a 5!\nLinha:");
                linha = entrada.next();
            }
            while(Integer.parseInt(linha) < 1 || Integer.parseInt(linha) > 5)
            {
                System.out.println("Apenas números inteiros de 1 a 5!\nLinha:");
                linha = entrada.next();
            }

            //Pede a coluna
            System.out.print("Coluna:");
            coluna = entrada.next();
            while(!isInt(coluna))
            {
                System.out.println("Apenas números inteiros de 1 a 5!\nColuna:");
                coluna = entrada.next();
            }
            while(Integer.parseInt(coluna) < 1 || Integer.parseInt(coluna) > 5)
            {
                System.out.println("Apenas números inteiros de 1 a 5!\nColuna:");
                coluna = entrada.next();
            }
            System.out.println("\n\n");

            //Tiro
            tab.tiro(Integer.parseInt(linha), Integer.parseInt(coluna));
            System.out.println();

        }while(tab.acertos != 3);
        System.out.println("\nVOCÊ DERROTOU O INIMIGO!!!! Turnos: " + tab.turno);
        if(tab.turno < tab.recorde)
        {
            tab.recorde = tab.turno;
            System.out.println("\nNOVO RECORDE (" + tab.recorde + ")!!!!");
        }
    }
}

Java board.

/* Água não descoberta(~)
   Água vazia(*): 0
   Navio(X): 1
*/
package batalhanaval;
import java.util.Random;

public class Tabuleiro
{
    int turno = 1; //Turno em questão
    int acertos = 0; //Armazena o número de acertos
    int recorde = 1000; //Armazena o recorde
    int[][] tab = new int[5][5]; //Tabuleiro
    int[][] tiros = new int[5][5]; //Armazeana as posições dos tiros dados: 0 para área desconhecida e 1 para área onde se atirou.

    // Inicia o tabuleiro com o valor padrão 0
    public void initTab()
    {
        acertos = 0;
        turno = 1;
        for(int[] i: tab)
        {
            for(int j = 0; j < i.length; j++)
            {
                i[j] = 0;
            }
        }
    }
    //Sorteia os navios
    public void initShips()
    {
        Random rand = new Random();
        int i; //Linha
        int j; //Coluna

        for(int n = 0; n < 3; n++)
        {
            do
            {
                i = rand.nextInt(5);
                j = rand.nextInt(5);  
            }while(tab[i][j] != 0);
            tab[i][j] = 1;
        }
    }
    //Mostra o tabuleiro de inteiros
    public void printTabInt()
    {
        System.out.print("     (1)  (2)  (3)  (4)  (5)\n\n");
        for(int i = 0; i < tab.length; i++)
        {
            System.out.print("("+ (i+1) + ")  ");
            for(int j = 0; j < tab[i].length; j++)
            {
                System.out.print(" "+tab[i][j]+"   ");
            }
            System.out.println("\n");
        }
    }
    //Mostra o tabuleiro de jogo
    public void printTab()
    {
        System.out.print("     (1)  (2)  (3)  (4)  (5)\n\n");
        for(int i = 0; i < tab.length; i++)
        {
            System.out.print("("+ (i+1) + ")  ");
            for(int j = 0; j < tab[i].length; j++)
            {
                if(tiros[i][j] == 1)
                {
                    if(tab[i][j] == 1)
                    {
                        System.out.print(" X   ");
                    }
                    else
                    {
                        System.out.print(" *   ");
                    }
                }
                else
                {
                    System.out.print(" ~   ");
                }
            }
            System.out.println("\n");
        }
    }
    //Tiro
    public void tiro(int linha, int coluna)
    {
        if(tiros[linha-1][coluna-1] == 0) //Checa se já foi dado tiro na posição fornecida
        {
            tiros[linha-1][coluna-1] = 1; //Muda o valor da posição fornecida para que conste como uma área atirada
            if(tab[linha-1][coluna-1] == 1) //Checa se há um navio na posição fornecida
            {
                System.out.println("Você afundou um navio inimigo!!!");
                acertos++; //Aumenta +1 em acertos
            }
            else
            {
                System.out.println("Você atingiu a água...");
            }
            turno++; //Avança o turno
        }
        else
        {
            System.out.println("Você já atirou nessa posição!");
        }
    }
    //Dica: informa o turno e se há alguma navio na linha e na coluna do último tiro disparado
    public void dica(int linha, int coluna)
    {
        System.out.println("Turno: " + turno);

        int countL = 0;
        for(int i: tab[linha-1])
        {
            if(i == 1)
            {
                countL++;
            }
        }
        System.out.println("Dica: há " + countL + " navio(s) na linha " + linha);

        int countC = 0;
        for(int[] i: tab)
        {
            if(i[coluna-1] == 1)
            {
                countC++;
            }
        }
        System.out.println("      há " + countC + " navio(s) na coluna " + coluna);
    }
}
  • 4

    If you can rewrite the question in English, I suggest moving it to http:/codereview.stackexchange.com, this site was made with the purpose of reviewing codes in the questions. You can also ask questions about something specific in relation to Stackoverflow performance (en, es, en, jp, etc)

  • 11

    Or you can wait to see if anyone can help, since the question is totally within the rules and scope of this site. The text could be more organized to favor those who try to help. Separating each part in a paragraph would help, but you could put yourself in the shoes of anyone who would help in a code that he’s never seen and try to pass on the information in a better way.

  • 1

    I agree with @bigown: your question is part of the scope of the site. It’s just that it’s hard to answer objectively. You mention "optimization" (which, by principle, I understand "performance"), but your game is pretty simple and doesn’t seem to have any serious performance issues. About how you implemented it, it is correct and serves you your need current.

  • 1

    Then you ask if someone would have done something different. Possibly. But a question that is perhaps more appropriate would be: how to make this code easier to evolve? (to include vessels with different shapes of only one cell, for example). Then the question ceases to be so opinionated, but it is still broad. I would start by representing each ship by a class and implement in that class the detection of "collision" (whether a shot hit or not). It can be a good exercise for you, and maybe even generate other questions from you here on the site. :)

1 answer

1

I believe you are researching to have more knowledge about good code practice, object orientation, automated testing and so on.

I suggest that you yourself have the critical sense to self-assess your code in order to look for flaws. Good programmers learn from their own mistake.

To try to elevate your knowledge, I will suggest some topics with links for you to delve into the subject. Here are some:

TDD (Test Driven Design):

Test-based software development. You evolve your code just for the test to pass. This makes it easy for you to build a better design of your architecture and objects, ensuring simplicity, low coupling and automatic coverage of your tests.

Object Orientation

Ensures that your system has classes with a single purpose and that they communicate with each other, ensuring their maintainability, usability and system evolution. If you are interested in the subject, I have created 10 rules to ensure that your code follows the good practices of object orientation based on the book "The Thoughtworks Anthology: Essays on Software".

https://github.com/matheusmessora/OOP-MANIFESTO


I will cite some examples of your code that violates some rules:

Rule 4: Only 1 point per line Improves the readability of code.

The code section below violates this rule, making it difficult to read.

tab.dica(Integer.parseInt(linha), Integer.parseInt(coluna));

A best practice would be Integer line = Integer.parseint(line); Integer column = Integer.parseint(column); tab.tip(row, column);

Rule 9: Method should have only 7 lines of code. This ensures that their methods have high cohesion, that is, they do only what they should do, and nothing else. His method printTab for example violates the rule.


Finally, I should add that every method must remain in the infinitive. This is a good practice adopted in many object-oriented languages. In this case, the method tab tip. should be amended to tab.displayDica(...)

  • 8

    Bons programadores aprendem com o próprio erro, True, the wicked not only insist on error, but don’t even recognize when they’re doing it. Bad programmers follow rules, do not understand what they are for, others go even further and invent rules completely without foundation. TDD has nothing to do with the question. And what makes building better systems is real knowledge and qualitative experience. Without this you can use the tool you want that will come out wrong thing. OOP does not guarantee anything, let alone what has been stated. Knowing how to make systems correctly and apply this knowledge helps...

  • 3

    ...something, but it also does not guarantee. OOP is not a panacea and in fact as few people know how to do right, it has more harm than help. There are other tools with similar goals. Good programmers know them and apply the best for each case, do not think that a single tool solves everything. In my opinion the most damaging answer that helps by putting things that have no relevance to the question, tendentiously based on unsubstantiated information and universally considered erroneous.

  • 3

    I’m sorry, but these 10 rules, in addition to generally having nothing to do with OOP, are all wrong, some for a small detail, others with serious error, because they’re saying to do the opposite of what you should. They’re all bad because they establish what to do without a context. None makes sense in general use, some make sense in something very specific, so specific that it could never be used in a text that tries to say what is good or not. I will not keep debating the subject, my goal is just to warn people that this response preaches a personal will and not something consolidated..

  • The "10 rules" are based on Jeff Bay’s "Object calisthenics". These are rules to be practiced to improve your Skills in programming, and yes, they should not be used in any project. There are N+1 solution to the above question, what I wanted to propose here was to open the mind of the questioner to new topics in order to raise his knowledge. http://www.javaworld.com/article/2081135/core-java/object-calisthenics-change-the-way-you-code.html

  • 4

    Note that there does not speak in OOP. Some of the more serious errors are not there. But there are still silly rules without explanation. The problem is that many people read these lists, know nothing about anything and believe that they are good and follow blindly because that’s what they have at hand. Although the intention is to help, often ends up harming.

  • Without getting into the merit of the quality of such a list, I disagree with the application of rule 4 in the example you provided. First, the line as it is is clear enough for those who know the language. Second, if the question is effort (typing Integer.parseInt for each parameter) because the method expects a different type from the one used, a better (clearer) "solution" would implement an overload of the method dica class tab. Or use the correct data type. Or already convert it at the time it is produced. Definitely, the exchange you suggest is 6 by half a dozen.

  • 1

    In fact, its rule 4 is conflicting with its rule 9, because following rule 4 is precisely increasing the number of rows (or columns, if you choose to put all in a single line) - which, according to rule 9, tends to decrease the "cohesion" (whose meaning you use is not clear).

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.