image

Acesse bootcamps ilimitados e +650 cursos

50
%OFF
Article image

MO

Marco Oliveira05/09/2025 02:42
Compartilhe

Um pequeno detalhe que se tornou grande: SudokuSwingApp!

    # Introdução

    O SudokuSwingApp nasceu como um projeto funcional e direto ao ponto: um tabuleiro de Sudoku com regras clássicas, interface gráfica simples e dois botões — **Verificar** e **Reiniciar**. A proposta inicial era entregar uma experiência básica, porém sólida, para o jogador.

    Mas, como acontece com muitos projetos movidos por paixão, um pequeno detalhe acabou mudando tudo.

    ## A virada inesperada: escolhendo Java Swing em vez do JavaFX

    Em vez de migrar para JavaFX, decidi seguir com o **Java Swing**. Sim, aquele mesmo que muitos consideram ULTRAPASSADO! Há... Há... HÁ! 

    Essa escolha exigiu mais tempo, paciência, criatividade e lógica — especialmente na validação de tarefas e ações. 

    Por outro lado, me proporcionou liberdade total para construir uma interface personalizada, com controle absoluto sobre cada componente.

    O resultado? Uma aplicação que evoluiu de simples para sofisticada, sem perder sua essência. 

    A busca por melhorias no SudokuSwingApp se tornou constante, com foco na adição de funcionalidades voltadas para **Interação**, **Usabilidade** e **Jogabilidade** — tudo pensado para o **usuário jogador** do tabuleiro Sudoku.

    Esses aprimoramentos não apenas tornaram o jogo mais acessível e intuitivo, como também elevaram o nível de imersão e desafio, respeitando o ritmo e estilo de cada jogador.

    ## O tempo investido para otimizar e obter um melhor desempenho do SudokuSwingApp

    Com o tempo, o SudokuSwingApp deixou de ser apenas um jogo. Ele se transformou em um **sistema otimizado**, para obter um melhor desempenho e projetado para oferecer:

    🔹 Interação fluida, com navegação por teclado e mouse 

    🔹 Usabilidade refinada, com suporte a múltiplos idiomas e atalhos inteligentes 

    🔹 Jogabilidade adaptativa, com quebra-cabeças progressivos e feedback claro ao jogador 

    Esses pilares redefiniram a experiência do jogador, tornando o SudokuSwingApp mais acessível, envolvente e prazeroso.

    ## Conclusão

    O SudokuSwingApp é a prova de que grandes mudanças podem nascer de pequenos detalhes. 

    O que começou como um projeto simples virou uma aplicação completa — graças à paixão pela carreira Full-Stack, à persistência e à vontade de oferecer algo melhor para o jogador do tabuleiro Sudoku. 

    Tudo isso movido por uma força superior e pelo desejo genuíno de criar algo significativo.

    # Bootcamp - DIO - Santander 2025 - Back-End com Java

    ## 🧠 Criando um Jogo do Sudoku em Java

    image

    **Projeto de Sudoku com interface gráfica usando Java e Swing.**

    A aplicação permite que o usuário jogue Sudoku diretamente em uma interface visual intuitiva, respeitando as regras clássicas do jogo.

    image

    ## 📦 Requisitos

    - Java 17 ou superior

    - IDE ou editor de código com suporte a Java (IntelliJ IDEA, Eclipse, VS Code)

    ## 🛠️ Tecnologias Utilizadas

    - Java 21

    - Swing (`javax.swing`)

    - AWT (`java.awt`)

    - Layouts: `GridLayout`, `BorderLayout`

    - Entrada com `JTextField` e `DocumentFilter`

    - Alertas com `JOptionPane`

    - Gerenciamento de status com `GameStatusEnum`

    - Argumentos via `args[]` para carregamento de puzzles

    ## 🚀 Execução da Aplicação

    Execute a classe `main.SudokuSwingApp.java` para iniciar a interface gráfica com a Tela de **Abertura** do jogo de Sudoku.

    ## 🖥️ Modos de Exibição

    O **SudokuSwingApp** oferece duas opções de visualização para o usuário jogador:

    - 🪟 **Modo Janela:** Inicia com tamanho padrão de **940 x 660 pixels**, podendo ser redimensionado ou maximizado pelo botão da janela

    - 🔲 **Modo Tela Cheia (Fullscreen):** Pressione `ALT + ENTER` para alternar entre modo janela e tela cheia, proporcionando uma experiência imersiva

    Para **sair do modo Tela Cheia**, o jogador pode pressionar novamente `ALT + ENTER` ou simplesmente apertar a tecla `Esc`, restaurando a interface gráfica para o modo janela.

    Esses modos garantem flexibilidade e conforto visual, adaptando-se a diferentes tipos de tela e preferências do jogador.

    ## 🚪 Sistema SudokuSwingApp

    ### 🔢 Introdução Animada

    Ao iniciar o aplicativo, o usuário é recebido por uma Tela de **Abertura** animada com o ícone do Tabuleiro Sudoku. 

    Essa introdução proporciona uma experiência visual agradável e profissional, preparando o jogador para acessar o **Menu Principal**.

    ### 🗂️ Tela do Menu Principal

    O Menu Principal do programa contém os seguintes botões:

    - **Jogar:** inicia uma nova partida no tabuleiro Sudoku

    - **Instruções:** exibe regras e orientações sobre o jogo

    - **Configurações:** permite escolher o idioma `Português (Brasil)` ou `Inglês (Estados Unidos)`

    - **Sobre:** exibe informações sobre a construção do projeto Sudoku com Java Swing

    - **Encerrar:** encerra o aplicativo (`Alt + F4`)

    ## 🌐 Idiomas disponíveis

    O SudokuSwingApp está disponível nos seguintes idiomas:

    - 🇧🇷 Português (Brasil)

    - 🇺🇸 English (United States)

    Na tela de **Configurações**, escolha o idioma que deseja utilizar com o **SudokuSwingApp**.

    ### ⌨️ Atalhos de Teclado

    O SudokuSwingApp oferece suporte para ativação dos botões via teclado, proporcionando uma navegação rápida, acessível e eficiente. 

    Essa funcionalidade é especialmente útil para usuários que preferem atalhos no teclado em vez do uso do mouse no programa.

    ### image BR — Português (Brasil) | image US — Inglês (Estados Unidos)

    | 📋 Menu Principal (BR) | ⌨️ Atalho | 📋 Main Menu (US) | ⌨️ Shortcut |
    |------------------------|------------|-------------------|--------------|
    | Jogar                  | `Alt + J`  | Play              | `Alt + P`    |
    | Instruções             | `Alt + I`  | Instructions      | `Alt + I`    |
    | Configurações          | `Alt + C`  | Settings          | `Alt + S`    |
    | Sobre                  | `Alt + S`  | About             | `Alt + A`    |
    | Encerrar               | `Alt + E`  | Exit              | `Alt + E`    |
    

    ## ⚙️ Funcionalidades do Tabuleiro Sudoku

    - Interface gráfica com tabuleiro 9×9

    - Células fixas e jogáveis

    - Atualização do progresso preenchido no rodapé da interface

    - Botão **Verificar** habilitado ao preencher todas as células

    - Detecção de erros: números repetidos em linhas, colunas e blocos

    - Borda vermelha para células com erro após a verificação

    - Botão **Solucionar** para revelar a solução correta do quebra-cabeça

    - Botão **Novo** que gera sequencialmente um novo puzzle da lista predefinida

    - Botão **Reiniciar** que restaura o tabuleiro atual ao seu estado original

    | 🧮 Tabuleiro Sudoku (BR) | ⌨️ Atalho | 🧮 Sudoku Board (US) | ⌨️ Shortcut |
    |--------------------------|------------|----------------------|--------------|
    | Verificar                | `Alt + V`  | Check                | `Alt + C`    |
    | Solucionar               | `Alt + S`  | Solve                | `Alt + S`    |
    | Novo                     | `Alt + N`  | New                  | `Alt + N`    |
    | Reiniciar                | `Alt + R`  | Restart              | `Alt + R`    |
    
    

    ## 🧑‍💻 Interação com o Tabuleiro

    **Modo Teclado:**

    - Use `TAB`, `SHIFT+TAB`, `ENTER`, ou setas direcionais para navegar,

    - `ARROW UP`, `ARROW DOWN`, `ARROW LEFT`, `ARROW RIGHT`.

    - Digite diretamente o número na célula editável.

    **Modo Mouse:**

    - Botão direito abre menu com números e opção **Limpar**.

    - Botão esquerdo confirma o número.

    - Clique duplo remove o número inserido.

    ## 🧩 Mecânica de Jogo

    - O sistema oferece **5 puzzles por ciclo**.

    - A dificuldade avança automaticamente do nível **iniciante** ao **mestre**.

    - Jogabilidade inteligente adaptada ao estilo do jogador.

    ## 💬 Feedback para o Usuário Jogador

    - ❌ Tabuleiro errado: 

     `Sudoku! Verifique se há números repetidos em linhas, colunas, ou blocos 3x3.`

    - ✅ Tabuleiro correto: 

     `Parabéns! Você completou corretamente o tabuleiro do jogo Sudoku.`

    **🎮 Divirta-se resolvendo e dominando o tabuleiro Sudoku!**

    **🎯 O desafio é justamente encontrar a combinação correta de números para completar o tabuleiro, seguindo as regras do jogo Sudoku.**

    ## 🔗 Explore o Projeto Sudoku no GitHub

    ### Método Principal Main

    Este método é responsável por iniciar a execução do programa. Ele define os parâmetros iniciais, chama as funções principais e gerencia o fluxo de controle.

    ### Estrutura do Código // SudokuSwingApp.java ☕

    package main;
    
    import model.Board;
    import model.BoardTemplate;
    import model.InvalidBoardFormatException;
    import model.PuzzleRepository;
    import util.SudokuLanguage;
    import util.SudokuSettings;
    import view.*;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.net.URL;
    import java.util.Locale;
    
    public class SudokuSwingApp {
    
    private static JFrame menuFrame;
    private static JFrame currentFrame;
    private static boolean isFullScreen;
    private static Rectangle windowBounds;
    private static final GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
    private static boolean shortcutsRegistered = false;
    private static int lastExtendedState = JFrame.NORMAL;
    
    public static void main(String[] args) {
      setLanguage(new Locale("pt", "BR"));
      new SudokuSplash().showSplash(5000);
      SudokuStyle.applyGlobalStyle();
      SudokuSound.initSounds();
      SwingUtilities.invokeLater(SudokuSwingApp::showMainMenu);
    }
    
    public static void setLanguage(Locale locale) {
      SudokuLanguage.setLocale(locale);
      if (currentFrame != null) updateFrameLanguage(currentFrame);
    }
    
    private static void updateFrameLanguage(JFrame frame) {
      Container content = frame.getContentPane();
      if (content instanceof SudokuMenu) {
        frame.setContentPane(new SudokuMenu());
      } else if (content instanceof SudokuSettings) {
        frame.setContentPane(new SudokuSettings());
      } else if (content instanceof SudokuInstructions) {
        frame.setContentPane(new SudokuInstructions());
      } else if (content instanceof SudokuPanel) {
        Board board = ((SudokuPanel) content).getBoard();
        frame.setContentPane(new SudokuPanel(board));
      } else if (content instanceof SudokuAbout) {
        frame.setContentPane(new SudokuAbout());
      }
      bindClickSoundToButtons(frame.getContentPane());
      frame.revalidate();
      frame.repaint();
    }
    
    public static void playClickSound() {
      SudokuSound.playClickSound();
    }
    
    private static void bindClickSoundToButtons(Container container) {
      for (Component comp : container.getComponents()) {
        if (comp instanceof JButton button) {
          ActionListener[] listeners = button.getActionListeners();
          for (ActionListener listener : listeners) {
            button.removeActionListener(listener);
            button.addActionListener(e -> {
              playClickSound();
              listener.actionPerformed(e);
            });
          }
        } else if (comp instanceof Container child) {
          bindClickSoundToButtons(child);
        }
      }
    }
    
    private static void showMainMenu() {
      if (menuFrame != null) menuFrame.dispose();
      menuFrame = new JFrame("Sudoku");
      currentFrame = menuFrame;
      menuFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      menuFrame.setLayout(new BorderLayout());
      applyAppIcon(menuFrame);
      menuFrame.getContentPane().setBackground(SudokuStyle.COLOR_PRIMARY);
      menuFrame.setContentPane(new SudokuMenu());
      bindClickSoundToButtons(menuFrame.getContentPane());
      applyPreviousWindowState(menuFrame);
      applyFullscreenIfActive(menuFrame);
      menuFrame.setVisible(true);
      menuFrame.requestFocus();
      menuFrame.toFront();
      setupWindowShortcuts(menuFrame);
    }
    
    public static void startGame() {
      Board board;
      try {
        board = BoardTemplate.of(PuzzleRepository.getPuzzleInicial()).build();
        board.getSpaces().forEach(row ->
            row.stream()
                .filter(space -> !space.isFixed())
                .forEach(space -> space.setActual(null))
        );
      } catch (InvalidBoardFormatException e) {
        JOptionPane.showMessageDialog(null, "Erro ao carregar o puzzle.", "Erro", JOptionPane.ERROR_MESSAGE);
        return;
      }
      showFrameWithContent("Sudoku", new SudokuPanel(board), SudokuSwingApp::showMainMenu);
    }
    
    private static void showFrameWithContent(String title, JComponent content, Runnable onClose) {
      int previousState = currentFrame != null ? currentFrame.getExtendedState() : JFrame.NORMAL;
      boolean wasFullScreen = isFullScreen;
    
      if (currentFrame != null) currentFrame.dispose();
    
      JFrame frame = new JFrame(title);
      currentFrame = frame;
      frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
      frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          frame.dispose();
          if (onClose != null) onClose.run();
        }
      });
    
      applyAppIcon(frame);
      frame.setLayout(new BorderLayout());
      frame.getContentPane().setBackground(SudokuStyle.COLOR_PRIMARY);
      frame.setContentPane(content);
      bindClickSoundToButtons(frame.getContentPane());
    
      if (wasFullScreen) {
        applyFullscreenIfActive(frame);
      } else {
        frame.setUndecorated(false);
        frame.setExtendedState(previousState);
        if (previousState == JFrame.NORMAL) {
          frame.setSize(new Dimension(940, 660));
          frame.setLocationRelativeTo(null);
        }
      }
    
      frame.setVisible(true);
      setupWindowShortcuts(frame);
    
      frame.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
          if (frame.getExtendedState() == JFrame.NORMAL) {
            Dimension size = frame.getSize();
            if (size.width < 940 || size.height < 660) {
              frame.setSize(940, 660);
              frame.setLocationRelativeTo(null);
            }
          }
        }
      });
    }
    
    public static void showInstructions() {
      showFrameWithContent("Sudoku", new SudokuInstructions(), () -> reopenMainMenu(currentFrame));
    }
    
    public static void showSettings() {
      showFrameWithContent("Sudoku", new SudokuSettings(), () -> reopenMainMenu(currentFrame));
    }
    
    public static void showAbout() {
      showFrameWithContent("Sudoku", new SudokuAbout(), () -> reopenMainMenu(currentFrame));
    }
    
    public static void reopenMainMenu(JFrame closingFrame) {
      saveWindowState(closingFrame);
      closingFrame.dispose();
      showMainMenu();
    }
    
    public static void applyAppIcon(JFrame frame) {
      URL iconUrl = SudokuSwingApp.class.getResource("/images/icons/sudoku_icon.png");
      if (iconUrl != null) {
        Image image = new ImageIcon(iconUrl).getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH);
        frame.setIconImage(image);
      }
    }
    
    private static void setupWindowShortcuts(JFrame frame) {
      if (shortcutsRegistered) return;
    
      KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> {
        if (e.getID() == KeyEvent.KEY_PRESSED && currentFrame != null) {
          if (e.isAltDown() && e.getKeyCode() == KeyEvent.VK_ENTER) {
            toggleFullscreen(currentFrame);
            return true;
          }
          if (e.getKeyCode() == KeyEvent.VK_ESCAPE && isFullScreen) {
            exitFullscreen(currentFrame);
            return true;
          }
          if (e.isControlDown()) {
            if (e.getKeyCode() == KeyEvent.VK_1) {
              applyWindowedMode(currentFrame);
              return true;
            }
            if (e.getKeyCode() == KeyEvent.VK_2) {
              applyMaximizedMode(currentFrame);
              return true;
            }
          }
        }
        return false;
      });
    
      shortcutsRegistered = true;
    }
    
    private static void applyWindowedMode(JFrame frame) {
      if (isFullScreen) {
        gd.setFullScreenWindow(null);
        isFullScreen = false;
      }
      frame.dispose();
      frame.setUndecorated(false);
      lastExtendedState = JFrame.NORMAL;
      applyPreviousWindowState(frame);
      frame.setVisible(true);
    }
    
    private static void applyMaximizedMode(JFrame frame) {
      if (isFullScreen) {
        gd.setFullScreenWindow(null);
        isFullScreen = false;
      }
      frame.dispose();
      frame.setUndecorated(false);
      lastExtendedState = JFrame.MAXIMIZED_BOTH;
      frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
      frame.setVisible(true);
    }
    
    private static void toggleFullscreen(JFrame frame) {
      if (!isFullScreen && gd.isFullScreenSupported()) {
        saveWindowState(frame);
        frame.dispose();
        frame.setUndecorated(true);
        gd.setFullScreenWindow(frame);
        frame.setVisible(true);
        isFullScreen = true;
      } else {
        exitFullscreen(frame);
      }
    }
    
    private static void exitFullscreen(JFrame frame) {
      gd.setFullScreenWindow(null);
      isFullScreen = false;
      frame.dispose();
      frame.setUndecorated(false);
      frame.setSize(new Dimension(940, 660));
      frame.setLocationRelativeTo(null);
      frame.setExtendedState(JFrame.NORMAL);
      frame.setVisible(true);
    }
    
    private static void applyFullscreenIfActive(JFrame frame) {
      if (isFullScreen && gd.isFullScreenSupported()) {
        frame.dispose();
        frame.setUndecorated(true);
        gd.setFullScreenWindow(frame);
        frame.setVisible(true);
      }
    }
    
    private static void saveWindowState(JFrame frame) {
      if (!isFullScreen) {
        windowBounds = frame.getBounds();
        lastExtendedState = frame.getExtendedState();
      }
    }
    
    private static void applyPreviousWindowState(JFrame frame) {
      if (!isFullScreen && windowBounds != null) {
        frame.setBounds(windowBounds);
        frame.setExtendedState(lastExtendedState);
      } else {
        frame.setSize(new Dimension(940, 660));
        frame.setLocationRelativeTo(null);
        frame.setExtendedState(JFrame.NORMAL);
      }
    }
    
    }
    
    

    Para mais detalhes e acesso ao código completo, visite o repositório do projeto sudoku criado em 03/09/2025 por mafigoliv (Marco Antonio) no GitHub image

    Compartilhe
    Recomendados para você
    Microsoft - Azure AZ-900
    Ri Happy - Front-end do Zero #2
    Avanade - Back-end com .NET e IA
    Comentários (0)