Movement restriction on GridLayout to only adjacent grid buttons - java

I am making a game where move across a gridlayout of jbuttons, only allowing movement to adjacent grid jbuttons. The goal is to try and make it to the topleft (From the btm right). I think this is all that is relevant:
//Here is my method to determine whether it is a valid move (i dont want you to be able to move anywhere but adjacent spots
public boolean goodMove(int x, int y) {
int val = Math.abs(current.x - x) + Math.abs(current.y - y);
return val == 1;
}
//Here is the logic in the actionlister, a big if else loop that is now referencing only the jbuttons in my gridlayout (the game panel)
public void actionPerformed(ActionEvent e) {
JButton clicked = (JButton) e.getSource();
if (clicked == SHOWMINES) {
if (SHOWMINES.getText().equals("Show Mines")) {
showMines();
SHOWMINES.setText("Hide Mines");
} else {
hideMines();
}
} else {
if (clicked == NEWGAME) {
newGame();
} else {
if (clicked == SHOWPATH) {
if (SHOWPATH.getText().equals("Show Path")) {
showPath();
SHOWPATH.setText("Hide Path");
} else {
hidePath();
}
} else {
if (clicked == OKAY) {
resetGridSize(Integer.parseInt(gridSizeInput.getText()));
newGame();
} else {
}
int gs = Integer.parseInt(gridSizeInput.getText());
for (int i = 0; i < gs - 1; i++) {
for (int j = 0; j < gs - 1; j++) {
if (clicked == grid[i][j]) {
if (goodMove(i, j)) {//debug tester ,this is where the problem is.
System.out.println("TEST");
current = new Point(i, j);
grid[i][j].setBackground(Color.green);
grid[i][j].setText("=");
}else{System.out.println("TEST2");}
}
}
}
}
}
}
}// end of action listener
Grid[][] is my array of grid buttons, gridSizeInput is the textfield that you can adjust the gridsize in.
Thanks in advance (I'm new to programming, so keep it simple if you can :)
EDIT: I forgot to mention, it is not recognizing my if(goodMove()) loop, its just printing out TWO for my else.
EDIT: My question is why is not working, no buttons will are be recognized as clicked in my grid, I guess I am asking to see if something I have written is glaringly obviously wrong. thanks.
EDIT: Okay here is my entire action listener for all my buttons. This is my first post, sorry I am not more helpful- let me know if anything else I can add.

Your problem may be as simple as your not extending your for loops out far enough. Try changing this:
for (int i = 0; i < gs - 1; i++) {
for (int j = 0; j < gs - 1; j++) {
to this:
for (int i = 0; i < gs; i++) {
for (int j = 0; j < gs; j++) {
Edit
This was the minimal code example that I created to test the concept:
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.*;
import javax.swing.*;
public class Foo extends JPanel {
public static final int GRID_SIZE = 10;
private Point current;
private JButton[][] grid;
public Foo() {
ButtonListener listener = new ButtonListener();
setLayout(new GridLayout(GRID_SIZE, GRID_SIZE));
grid = new JButton[GRID_SIZE][GRID_SIZE];
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[i].length; j++) {
grid[i][j] = new JButton(" ");
grid[i][j].addActionListener(listener);
add(grid[i][j]);
}
}
current = new Point(GRID_SIZE - 1, GRID_SIZE - 1);
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JButton clicked = (JButton) e.getSource();
// !! int gs = Integer.parseInt(gridSizeInput.getText());
int gs = GRID_SIZE;
// !! for (int i = 0; i < gs - 1; i++) {
// for (int j = 0; j < gs - 1; j++) {
for (int i = 0; i < gs; i++) {
for (int j = 0; j < gs; j++) {
if (clicked == grid[i][j]) {
if (goodMove(i, j)) {
System.out.println("TEST");
current = new Point(i, j);
grid[i][j].setBackground(Color.green);
grid[i][j].setText("=");
} else {
System.out.println("TEST2");
}
}
}
}
}
}
public boolean goodMove(int x, int y) {
int val = Math.abs(current.x - x) + Math.abs(current.y - y);
return val == 1;
}
private static void createAndShowGui() {
Foo mainPanel = new Foo();
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Related

Scrolling text effect on 2D array

For a project I am working on I would like to be abbe to "scroll" an array like so:
Here is the code I have so far:
private boolean[][] display = this.parseTo8BitMatrix("Here");
private int scroll = 0;
public void scroll() {
this.scroll++;
}
//sets the clock's display
public void setDisplay(String s) {
this.display = this.parseTo8BitMatrix(s);
}
//determines the current frame of the clock
private boolean[][] currentFrame() {
boolean[][] currentFrame = new boolean[8][32];
int length = this.display[0].length;
if(length == 32) { //do nothing
currentFrame = this.display;
} else if(length <= 24) { //center
for(int i = 0; i < 8; i++) {
for(int j = 0; j < length; j++) {
currentFrame[i][j+((32-length)/2)] = this.display[i][j];
}
}
this.display = currentFrame; //set display to currentFrame so the display doesn't get centered each time
} else { //scroll
for(int i = 0; i < 8; i++) {
for(int j = 0; j < length; j++) {
if(this.scroll+j <= 32) {
currentFrame[i][j] = this.display[i][j+this.scroll];
} else {
//?
}
}
}
}
return currentFrame;
}
The code I have is effective up until the the array needs to "wrap around" to the other side. Where have I gone wrong?
I'm assuming that you're looking for a formula that would work for the else.
Usually Modulos are very helpful for wrapping around.
What you are looking for is basically
currentFrame[i][j]= this.display[i][(j+this.scroll)%length];
which works even when it's not wrapped around.

Game loop of a turn-based game

Currently, I'm working on an AI for a simple turn-based game. The way I have the game set up is as following (in pseudo-code):
players = [User, AI];
(for player : players){
player.addEventlistener(MoveListener (moveData)->move(moveData));
}
players[game.getTurn()].startTurn();
the move function:
move(data){
game.doStuff(data);
if(game.isOver())
return;
game.nextTurn();
players[game.getTurn()].startTurn();
}
This results in the following recursion:
start turn
player/AI makes a move
move function gets called
the next player starts their turn
...
This repeats until the game is over - note that the game is of finite length and doesn't go past ~50 moves. Now, even though the recursion is finite, I get a stackoverflow error. My question is: is there any way to fix this? Is there something wrong with the recursion after all? Or should I implement a game loop instead? I understand how this would work if AIs were to play against each other, but how would this work if the program had to wait for user input?
EDIT
Here are the relevant classes to the recursion:
Connect4 class:
package connect4;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Connect4 extends Application {
Group root = new Group();
GameSquare[][] squares;
GameButton[] buttons;
int currentTurn;
int columns = 7;
int rows = 6;
Text gameState;
Player[] players;
Game game;
#Override
public void start(Stage primaryStage) {
int size = 50;
int padding = 10;
gameState = new Text();
gameState.setX(padding);
gameState.setY((rows+1)*size+(rows+3)*padding);
root.getChildren().add(gameState);
buttons = new GameButton[columns];
for(int i = 0; i < buttons.length; i++){
buttons[i] = new GameButton(i);
buttons[i].setMaxWidth(size);
buttons[i].setMaxHeight(size);
buttons[i].setLayoutX(i*size+(i+1)*padding);
buttons[i].setLayoutY(padding);
buttons[i].setMouseTransparent(true);
buttons[i].setVisible(false);
root.getChildren().add(buttons[i]);
}
players = new Player[2];
players[0] = new UserControlled(buttons);
players[1] = new AI();
MoveListener listener = (int i) -> {move(i);};
for(Player player : players)
player.addListener(listener);
game = new Game(columns, rows, players.length);
squares = new GameSquare[columns][rows];
for(int x = 0; x < columns; x++){
for(int y = 0; y < rows; y++){
squares[x][y] = new GameSquare(
x*size+(x+1)*padding,
(y+1)*size+(y+2)*padding,
size,
size,
size,
size
);
root.getChildren().add(squares[x][y]);
}
}
players[game.getTurn()].startTurn(game);
updateTurn();
updateSquares();
draw(primaryStage);
}
public void move(int i){
game.move(i);
updateSquares();
if(game.isGameOver()){
if(game.isTie()){
tie();
return;
} else {
win();
return;
}
}
updateTurn();
players[game.getTurn()].startTurn(game);
}
private void updateSquares(){
int[][] board = game.getBoard();
for(int x = 0; x < columns; x++){
for(int y = 0; y < rows; y++){
squares[x][y].setOwner(board[x][y]);
}
}
}
private void updateTurn(){
gameState.setText("Player " + game.getTurn() + "'s turn");
}
public static void main(String[] args) {
launch(args);
}
private void draw(Stage primaryStage){
Scene scene = new Scene(root, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
private void win(){
gameState.setText("Player " + game.getWinner() + " has won the game!");
}
private void tie(){
gameState.setText("It's a tie!");
}
}
Game class:
package connect4;
public class Game {
private int turn = 0;
private int[][] board;
private int columns;
private int rows;
private int players;
private boolean gameOver = false;
private boolean tie = false;
private int winner = -1;
public Game(int columns, int rows, int playerCount){
this.columns = columns;
this.rows = rows;
board = new int[columns][rows];
for(int x = 0; x < columns; x++){
for(int y = 0; y < rows; y++){
board[x][y] = -1;
}
}
players = playerCount;
}
public int[][] getBoard(){
return board;
}
public int getTurn(){
return turn;
}
private void updateTurn(){
turn++;
if(turn >= players)
turn = 0;
}
public boolean isGameOver(){
return gameOver;
}
private void win(int player){
gameOver = true;
winner = player;
}
public int getWinner(){
return winner;
}
private void tie(){
gameOver = true;
tie = true;
}
public boolean isTie(){
return tie;
}
public void move(int i){
if(gameOver)
return;
if(columnSpaceLeft(i) == 0){
return;
}
board[i][columnSpaceLeft(i)-1] = turn;
checkWin(turn);
checkFullBoard();
if(gameOver)
return;
updateTurn();
}
private void checkFullBoard(){
for(int i = 0; i < columns; i++){
if(columnSpaceLeft(i) != 0)
return;
}
tie();
}
public int columnSpaceLeft(int column){
for(int i = 0; i < board[column].length; i++){
if(board[column][i] != -1)
return i;
}
return board[column].length;
}
public int[] getAvailableColumns(){
int columnCount = 0;
for(int i = 0; i < board.length; i++){
if(columnSpaceLeft(i) != 0)
columnCount++;
}
int[] columns = new int[columnCount];
int i = 0;
for(int j = 0; j < board.length; j++){
if(columnSpaceLeft(i) != 0){
columns[i] = j;
i++;
}
}
return columns;
}
private Boolean checkWin(int player){
//vertical
for(int x = 0; x < columns; x++){
int count = 0;
for(int y = 0; y < rows; y++){
if(board[x][y] == player)
count++;
else
count = 0;
if(count >= 4){
win(player);
return true;
}
}
}
//horizontal
for(int y = 0; y < rows; y++){
int count = 0;
for(int x = 0; x < columns; x++){
if(board[x][y] == player)
count++;
else
count = 0;
if(count >= 4){
win(player);
return true;
}
}
}
//diagonal
for(int x = 0; x < columns; x++){
for(int y = 0; y < rows; y++){
int count = 0;
//diagonaal /
if(!(x > columns-4 || y < 3) && board[x][y] == player){
count ++;
for(int i = 1; i <= 3; i++){
if(board[x+i][y-i] == player){
count++;
if(count >= 4){
win(player);
return true;
}
} else {
count = 0;
break;
}
}
}
//diagonal \
if(!(x > columns-4 || y > rows-4) && board[x][y] == player){
count ++;
for(int i = 1; i <= 3; i++){
if(board[x+i][y+i] == player){
count++;
if(count >= 4){
win(player);
return true;
}
} else {
count = 0;
break;
}
}
}
}
}
return false;
}
}
UserControlled class:
package connect4;
import java.util.ArrayList;
import java.util.List;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
public class UserControlled implements Player {
private List<MoveListener> listeners = new ArrayList<MoveListener>();
private GameButton[] buttons;
private boolean active = false;
public UserControlled(GameButton[] buttons){
this.buttons = buttons;
}
#Override
public void addListener(MoveListener listener){
listeners.add(listener);
}
#Override
public void startTurn(Game game){
System.out.println(0);
active = true;
for(int i = 0; i < buttons.length; i++){
if(game.columnSpaceLeft(i) != 0){
setButton(i, true);
buttons[i].setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
move(( (GameButton) e.getTarget()).getColumn());
}
});
}
}
}
private void move(int i){
if(!active)
return;
active = false;
disableButtons();
for(MoveListener listener : listeners)
listener.onMove(i);
}
private void disableButtons(){
for(int i = 0; i < buttons.length; i++){
setButton(i, false);
}
}
private void setButton(int i, boolean enable){
if(enable){
buttons[i].setMouseTransparent(false);
buttons[i].setVisible(true);
} else {
buttons[i].setMouseTransparent(true);
buttons[i].setVisible(false);
}
}
}
The AI class is basically the same as a stripped down UserControlled class, except the startTurn method:
int[] columns = game.getAvailableColumns();
move(columns[rng.nextInt(columns.length)]);
The MoveListener interface is very simple:
public interface MoveListener {
void onMove(int i);
}
The stack trace:
Exception in thread "JavaFX Application Thread" java.lang.StackOverflowError
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:142)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
at javafx.scene.text.Text.setText(Text.java:370)
//note that the three lines above this are different every time
//as the application crashes at a different point
at connect4.Connect4.updateTurn(Connect4.java:107)
at connect4.Connect4.move(Connect4.java:93)
at connect4.Connect4.lambda$start$0(Connect4.java:49)
at connect4.AI.move(AI.java:13)
at connect4.AI.startTurn(AI.java:24)
at connect4.Connect4.move(Connect4.java:94)
at connect4.Connect4.lambda$start$0(Connect4.java:49)
at connect4.AI.move(AI.java:13)
...etc
In general, you shouldn't use a recursion except you are pretty sure about what you are doing.
Think this, every time you call the next step, you are saving all the context, with all the local variables in the stack. In a game, it could be a lot of stuff.
A common game loop in a turn based game would be something like:
while(!gameFinished()){
for(player in players){
player.doTurn();
}
}
Take into account too, that recursion is slow, because it has to save all the context and it takes time, so, in general, think three times before trying to use a recursion.
EDIT
To process the input you could use something like this:
CompletableFuture.supplyAsync(this::waitUserInput)
.thenAccept(this::processUserInput)
Here you can find how it works:
http://www.deadcoderising.com/java8-writing-asynchronous-code-with-completablefuture/
With this, your code keeps running, so keep in mind that in the next line of code you won't have the input. When it gets the input, it will call the proccessUserInput method.
Another way to do this, is to check every frame if any key was pressed, and that's okay too.
Here you can find a way to do that:
How do I check if the user is pressing a key?
The way you should do things depends on the size of your project. If you will be checking for key presses all the time, maybe it's a good idea to build some sort of event system for that.
In the other hand, I recommend you to use a game engine like Unreal or Unity. If you want to keep with Java, there are a lot of libraries for games that handle a lot of common problems like this.
For example:
https://www.lwjgl.org/
You can find a lot of tutorials of turn-based games made with that library.
Good luck!

Remembering coordinates for chess game from the inner to outer class

So basically, I want to save all the coordinates of all row numbers that are processed in the actionPerformed method in the ButtonListener class to my Board class in order to move pieces for chess. When I run the program, the inner class ButtonListener saves all the variables which is good. But the variables do not keep the saved values when they are called to my constructor in class Board. I want to keep the values in the variables from the inner class to the outer class.
Edit:Provided the entire class.
public class Board extends JPanel {
Piece movingPiece;
ImageIcon image;
private int row1;
private int col1;
private int row2;
private int col2;
private JButton btn[][];
private Square squares[][];
public Board(String gameType, int row, int col) {
setLayout(new GridLayout(8, 8));
squares = new Square[row][col];
btn = new JButton[row][col];
setUpSquares();
setUpBtns();
setUpChessPieces();
findPieces();
movePiece(row1, col1, row2, col2);// all 0's
}
public void setUpSquares() {
for (int i = 0; i < ChessGame.EIGHT; i++) {
for (int j = 0; j < ChessGame.EIGHT; j++) {
squares[i][j] = new Square();
}
}
}
public void setUpBtns() {
for (int i = 0; i < ChessGame.EIGHT; i++) {
for (int j = 0; j < ChessGame.EIGHT; j++) {
btn[i][j] = new JButton();
add(btn[i][j]);
btn[i][j].addActionListener(new ButtonListener());
if ((i + j) % 2 == 0) {
btn[i][j].setBackground(Color.WHITE);
btn[i][j].setForeground(Color.WHITE);
} else {
btn[i][j].setBackground(Color.DARK_GRAY);
btn[i][j].setForeground(Color.DARK_GRAY);
}
}
}
}
public void movePiece(int row1, int col1, int row2, int col2) {
squares[row1][col1].getPiece().setRow(row2);
squares[row1][col1].getPiece().setCol(col2);
movingPiece = squares[row1][col1].getPiece();
squares[row2][col2].setPiece(movingPiece);
btn[row2][col2].setIcon(new ImageIcon(squares[row2][col2].getPiece().getPieceColor()));
}
/**
* Finds pieces and sets the piece icons to the button.
*/
public void findPieces() {
for (int i = 0; i < ChessGame.EIGHT; i++) {
for (int j = 0; j < ChessGame.EIGHT; j++) {
if (squares[i][j].getPiece() != null) {
btn[i][j].setIcon(new ImageIcon(squares[i][j].getPiece().getPieceColor()));
} else {
btn[i][j].setIcon(null);
}
}
}
}
public void setUpChessPieces() {
// white pieces
for (int i = 0; i < ChessGame.EIGHT; i++) {
Pawn pawn1 = new Pawn(1, i, 1, "white");
squares[1][i].setPiece(pawn1);
}
for (int i = 0; i < ChessGame.EIGHT; i += 7) {
Rook rook1 = new Rook(0, i, 1, "white");
squares[0][i].setPiece(rook1);
}
for (int i = 1; i < ChessGame.EIGHT; i += 5) {
Knight knight1 = new Knight(0, i, 1, "white");
squares[0][i].setPiece(knight1);
}
for (int i = 2; i < ChessGame.EIGHT; i += 3) {
Bishop bishop1 = new Bishop(0, i, 1, "white");
squares[0][i].setPiece(bishop1);
}
King king1 = new King(0, 4, 1, "white");
squares[0][4].setPiece(king1);
Queen queen1 = new Queen(0, 3, 1, "white");
squares[0][3].setPiece(queen1);
// black pieces
for (int i = 0; i < ChessGame.EIGHT; i++) {
Pawn pawn2 = new Pawn(6, i, 2, "black");
squares[6][i].setPiece(pawn2);
}
for (int i = 0; i < ChessGame.EIGHT; i += 7) {
Rook rook2 = new Rook(7, i, 1, "black");
squares[7][i].setPiece(rook2);
}
for (int i = 1; i < ChessGame.EIGHT; i += 5) {
Knight knight2 = new Knight(7, i, 1, "black");
squares[7][i].setPiece(knight2);
}
for (int i = 2; i < ChessGame.EIGHT; i += 3) {
Bishop bishop2 = new Bishop(7, i, 1, "black");
squares[7][i].setPiece(bishop2);
}
King king2 = new King(7, 4, 1, "black");
squares[7][4].setPiece(king2);
Queen queen2 = new Queen(7, 3, 1, "black");
squares[7][3].setPiece(queen2);
}
public class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JButton src = (JButton) e.getSource();
if (src.getBackground() != Color.DARK_GRAY && src.getBackground() != Color.WHITE) {
src.setBackground(src.getForeground());
} else {
src.setBackground(Color.MAGENTA);
}
for (int i = 0; i < ChessGame.EIGHT; i++) {
for (int j = 0; j < ChessGame.EIGHT; j++) {
if (squares[i][j].getPiece() != null) {
if (btn[i][j] == src) {
row1 = i;
col1 = j;
}
}
if (squares[i][j].getPiece() == null) {
if (btn[i][j] == src) {
row2 = i;
col2 = j;
}
}
}
}
System.out.println("row1:" + row1 + " col1:" + col1);
System.out.println("row2:" + row2 + " col2:" + col2);
}
}
}
Your problem is that you are calling the movePiece(int, int, int, int) from the constructor, and 2 buttons have not necessarily been clicked. The listener has been added to each button, but you don't know that the listener has been activated at that point. So, row1, col1, row2, and col2 are all set to the value that ints take when they have not been initiated yet, 0. So, you really want to call the move method from the listener. Something like:
btn[i][j].addActionListener(e -> {
JButton src = (JButton) e.getSource();
//Process src to get row1, col1, row2, and col2 as you did
movePiece(row1, col1, row2, col2);
});
I replaced your ButtonListener class with a lambda expression, but you can leave it as you did. The important part is that you are calling movePiece from the action listener because it ensures that row1, col1, row2, and col2 are all defined.

Sleep or wait time on every call to method from main in Swing class

I coded a Swing class where in the main there is only one call to a method solve(). This method implements a while loop and uses another method in almost every round, this method is called paint() and changes the value of only one JPanel inside of a 9x9 GridLayout, whose reference is stored in a 2D array.
I need a timer inisde paint() so before the panel is modified, 2 seconds pass. It does this the first time is called, but after the 2 seconds it does every round of the loop without waiting. The goal is to watch 2 seconds pass before any panel is modified. I tried using javax Timer. Thread.sleep() doesn't work in a Swing class apparently.
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Stack;
public class SudokuApp {
private JFrame frame;
int i = 9;
int j = 9;
JPanel[][] board = new JPanel[i][j];
JButton nextButton = new JButton("Resolver");
Box[][] sudoku = new Box[9][9];
Box newCurrent = new Box();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SudokuApp window = new SudokuApp();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SudokuApp() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setTitle("Sudoku Solver");
frame.setBounds(100, 100, 510, 555);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(new GridLayout(i, j));
//Initiate board
for (int m = 0; m < i; m++) {
for (int n = 0; n < j; n++) {
board[m][n] = new JPanel();
JTextField textField = new JTextField(1);
textField.setFont(new Font("Lucida Grande", Font.BOLD, 25));
board[m][n].add(textField);
panel.add(board[m][n]);
}
}
Box last = new Box();
for (int m = 0; m < this.i; m++) {
for (int n = 0; n < this.j; n++) {
sudoku[m][n] = new Box(m, n);
if (m != 0 || n != 0) sudoku[m][n].back = last;
last = sudoku[m][n];
}
}
//Solve button action listener
nextButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
if (validBoard()) {
nextButton.setEnabled(false);
solve();
}
else JOptionPane.showMessageDialog(null, "Condiciones iniciales inválidas.");
} });
frame.getContentPane().add(nextButton, BorderLayout.SOUTH);
}
private boolean validBoard() {
for (int m = 0; m < i; m++) {
for (int n = 0; n < j; n++) {
JTextField current = (JTextField) board[m][n].getComponent(0);
String text = current.getText();
if (text.length() == 1 && !text.contains("0")) {
try {
int number = Integer.parseInt(text);
if (!add(number, m, n)) return false;
current.setEditable(false);
} catch (NumberFormatException e) {
return false;
}
} else {
current.setText("");
current.setEditable(false);
}
}
}
return true;
}
private void solve() {
int i = 0;
int j = 0;
int newI = i + 1;
while (i < 9) {
while (j < 9) {
Box current = sudoku[i][j];
if (current.number == 0) {
HashSet<Integer> possibles = new HashSet<>();
HashSet<Integer> takenNumbers = getTakenNumbers(i, j);
for (int k = 1; k <= 9; k++) if (!takenNumbers.contains(k)) possibles.add(k);
if (possibles.isEmpty()) {
current.number = 0;
erase(i, j);
tryOther(current.back);
i = newCurrent.i;
newI = i + 1;
j = newCurrent.j + 1;
} else {
for (Integer p : possibles) current.possibles.push(p);
current.number = (int) current.possibles.pop();
paint(current.number, i, j);
j++;
newI = i + 1;
}
} else {
j++;
newI = i + 1;
}
}
i = newI;
j = 0;
}
}
private void tryOther(Box box) {
if (box.possibles.empty()) {
if (box.back != null) {
if (!box.initial) {
box.number = 0;
erase(box.i, box.j);
}
tryOther(box.back);
}
else newCurrent = null;
} else {
if (getTakenNumbers(box.i, box.j).contains(box.possibles.peek())) {
box.possibles.pop();
tryOther(box);
}
newCurrent = box;
box.number = (int) box.possibles.pop();
paint(box.number, box.i, box.j);
}
}
private boolean add(int a, int i, int j) {
if (getTakenNumbers(i, j).contains(a)) return false;
sudoku[i][j].number = a;
sudoku[i][j].initial = true;
return true;
}
private void paint(int a, int i, int j) {
JPanel panel = board[i][j];
JTextField jTextField = (JTextField) board[i][j].getComponent(0);
jTextField.setFont(new Font("Lucida Grande", Font.PLAIN, 25));
jTextField.setForeground(Color.gray);
ActionListener task = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("T");
}
};
Timer timer = new Timer(3000 , task);
timer.setRepeats(false);
timer.start();
jTextField.setText("" + a);
panel.revalidate();
panel.repaint();
}
private void erase(int i, int j) {
JTextField jTextField = (JTextField) board[i][j].getComponent(0);
jTextField.setText("");
board[i][j].revalidate();
board[i][j].repaint();
}
private HashSet<Integer> getTakenNumbers(int i, int j) {
HashSet<Integer> takenNumbers = new HashSet<>();
for (int k = 0; k < 9; k++) if (sudoku[i][k].number != 0 && k != j) takenNumbers.add(sudoku[i][k].number);
for (int k = 0; k < 9; k++) if (sudoku[k][j].number != 0 && k != i) takenNumbers.add(sudoku[k][j].number);
int bigBoxRow = 0;
int bigBoxColumn = 0;
if (i <= 5 && i > 2) bigBoxRow = 3;
if (i <= 8 && i > 5) bigBoxRow = 6;
if (j <= 5 && j > 2) bigBoxColumn = 3;
if (j <= 8 && j > 5) bigBoxColumn = 6;
for (int k = bigBoxRow; k < bigBoxRow + 3; k++) for (int l = bigBoxColumn; l < bigBoxColumn + 3; l++) if (sudoku[k][l].number != 0 && k != i && l != j) takenNumbers.add(sudoku[k][l].number);
return takenNumbers;
}
}
import java.util.Stack;
public class Box {
public int number = 0;
public int i, j;
public Stack possibles = new Stack();
public Box back;
public boolean initial = false;
public Box(int i, int j) {
this.i = i;
this.j = j;
}
public Box() {}
}
You need to change the structure of your program. Specifically, you will want to get rid of the nested while loops within your solve method and instead replace it with a single Swing Timer. That Timer will repeat 9 * 9 times, and will have a counter field inside of it, and inside of it you will want to do the code that you currently do within your nested while loops.
It could look something like...
// TIMER_DELAY could be 2000 if you want a 2 second delay
new Timer(TIMER_DELAY, new ActionListener() {
private int i = 0;
private int j = 0;
#Override
public void actionPerformed(ActionEvent e) {
// MAX_ROWS is 9
if (i == MAX_ROWS) {
// we've iterated through the whole thing
((Timer) e.getSource()).stop();
}
if (j == MAX_ROWS) {
// we've gone through all the columns
i++; // the next row
j = 0; // start at column 0
}
// TODO the code in the nested while loop.
j++;
}
}).start();
For an example of a functioning program that places text in fields with a delay (but doesn't solve anything):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SudokuMCVE extends JPanel {
private static final int CLUSTER = 3;
private static final int MAX_ROWS = 9;
private static final float FIELD_PTS = 32f;
private static final int GAP = 3;
private static final Color BG = Color.BLACK;
private static final Color SOLVED_BG = Color.LIGHT_GRAY;
public static final int TIMER_DELAY = 2 * 1000;
private JTextField[][] fieldGrid = new JTextField[MAX_ROWS][MAX_ROWS];
public SudokuMCVE() {
JPanel mainPanel = new JPanel(new GridLayout(CLUSTER, CLUSTER));
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.setBackground(BG);
JPanel[][] panels = new JPanel[CLUSTER][CLUSTER];
for (int i = 0; i < panels.length; i++) {
for (int j = 0; j < panels[i].length; j++) {
panels[i][j] = new JPanel(new GridLayout(CLUSTER, CLUSTER, 1, 1));
panels[i][j].setBackground(BG);
panels[i][j].setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(panels[i][j]);
}
}
for (int row = 0; row < fieldGrid.length; row++) {
for (int col = 0; col < fieldGrid[row].length; col++) {
fieldGrid[row][col] = createField(row, col);
int i = row / 3;
int j = col / 3;
panels[i][j].add(fieldGrid[row][col]);
}
}
setLayout(new BorderLayout());
add(mainPanel, BorderLayout.CENTER);
add(new JButton(new SolveAction("Solve")), BorderLayout.PAGE_END);
}
private JTextField createField(int row, int col) {
JTextField field = new JTextField(2);
field.setHorizontalAlignment(JTextField.CENTER);
field.setFont(field.getFont().deriveFont(Font.BOLD, FIELD_PTS));
return field;
}
private class SolveAction extends AbstractAction {
public SolveAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
new Timer(TIMER_DELAY, new ActionListener() {
private int i = 0;
private int j = 0;
#Override
public void actionPerformed(ActionEvent e) {
// MAX_ROWS is 9
if (i == MAX_ROWS) {
((Timer) e.getSource()).stop();
}
if (j == MAX_ROWS) {
i++;
j = 0;
}
int number = (int) (MAX_ROWS * Math.random()) + 1;
fieldGrid[i][j].setBackground(SOLVED_BG);
fieldGrid[i][j].setText(String.valueOf(number));
j++;
}
}).start();
}
}
private static void createAndShowGui() {
SudokuMCVE mainPanel = new SudokuMCVE();
JFrame frame = new JFrame("SudokuMCVE");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}

Java Swing Help

I'm trying to create a grid of JButtons, with index values to the left and right of the button grid.
Here is my code, but I'm getting a NullPointerException. What is the problem?
public class Test {
public static void main(String args[]) {
JFrame myapp = new JFrame("test");
myapp.setLayout(new FlowLayout());
myapp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myapp.setSize(400,400);
myapp.setVisible(true);
myapp.getContentPane().add(Board());
}
private static JPanel Board() {
JButton[][] buttons = new JButton [10][10];
JPanel board = new JPanel(new GridLayout(11,11));
String[] rowlabels = {" ","A","B","C","D","E","F","G","H","I","J"};
String[] columnlabels = {" ","1","2","3","4","5","6","7","8","9","10"};
JTextField k;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if ((j != 0) && (i != 0)) {
//myboard[i-1][j-1].addActionListener(new ButtonHandler());
board.add(buttons[i-1][j-1]);
}
if (i == 0) {
if (j != 0) {
// used to display row of numbers
k = new JTextField(columnlabels[j]);
k.setEditable(false);
k.setHorizontalAlignment((int) JFrame.CENTER_ALIGNMENT);
} else {
// used to display column of numbers
k = new JTextField();
k.setEditable(false);
}
board.add(k);
} else if (j == 0) {
k = new JTextField(rowlabels[i]);
k.setEditable(false);
k.setHorizontalAlignment((int) JFrame.CENTER_ALIGNMENT);
board.add(k);
}
}
}
return board;
}
}
I'm probably doing something obvious, but Swing is kind of new to me, so any help would be greatly appreciated.
Swing GUIs should be constructed on the EDT. That part is left as an exercise for the OP.
import java.awt.*;
import javax.swing.*;
public class Test {
public static void main(String args[]) {
JFrame myapp = new JFrame("test");
myapp.setLayout(new FlowLayout());
myapp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myapp.getContentPane().add(Board());
// very important!
myapp.pack();
// do these last.
myapp.setSize(400,400);
myapp.setVisible(true);
}
private static JPanel Board() {
JButton[][] buttons = new JButton [10][10];
for(int x=0; x<buttons.length; x++) {
for (int y=0; y<buttons[0].length; y++) {
buttons[x][y] = new JButton();
}
}
JPanel board = new JPanel(new GridLayout(11,11));
String[] rowlabels = {" ","A","B","C","D","E","F","G","H","I","J"};
String[] columnlabels = {" ","1","2","3","4","5","6","7","8","9","10"};
JTextField k;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if ((j != 0) && (i != 0)) {
//myboard[i-1][j-1].addActionListener(new ButtonHandler());
board.add(buttons[i-1][j-1]);
}
if (i == 0) {
if (j != 0) {
// used to display row of numbers
k = new JTextField(columnlabels[j]);
k.setEditable(false);
k.setHorizontalAlignment((int) JFrame.CENTER_ALIGNMENT);
} else {
// used to display column of numbers
k = new JTextField();
k.setEditable(false);
}
board.add(k);
} else if (j == 0) {
k = new JTextField(rowlabels[i]);
k.setEditable(false);
k.setHorizontalAlignment((int) JFrame.CENTER_ALIGNMENT);
board.add(k);
}
}
}
return board;
}
}
Given the nature of the GUI, I guess you are after something more like this.
import java.awt.*;
import javax.swing.*;
public class Test {
public static void main(String args[]) {
JFrame myapp = new JFrame("test");
myapp.setLayout(new FlowLayout());
myapp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myapp.getContentPane().add(Board());
// very important!
myapp.pack();
// do this last.
myapp.setVisible(true);
}
private static JPanel Board() {
JButton[][] buttons = new JButton [10][10];
for(int x=0; x<buttons.length; x++) {
for (int y=0; y<buttons[0].length; y++) {
JButton temp = new JButton();
temp.setPreferredSize(new Dimension(30,30));
buttons[x][y] = temp;
}
}
JPanel board = new JPanel(new GridLayout(11,11));
String[] rowlabels = {" ","A","B","C","D","E","F","G","H","I","J"};
String[] columnlabels = {" ","1","2","3","4","5","6","7","8","9","10"};
JTextField k;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if ((j != 0) && (i != 0)) {
//myboard[i-1][j-1].addActionListener(new ButtonHandler());
board.add(buttons[i-1][j-1]);
}
if (i == 0) {
if (j != 0) {
// used to display row of numbers
k = new JTextField(columnlabels[j]);
k.setEditable(false);
k.setHorizontalAlignment((int) JFrame.CENTER_ALIGNMENT);
} else {
// used to display column of numbers
k = new JTextField();
k.setEditable(false);
}
board.add(k);
} else if (j == 0) {
k = new JTextField(rowlabels[i]);
k.setEditable(false);
k.setHorizontalAlignment((int) JFrame.CENTER_ALIGNMENT);
board.add(k);
}
}
}
return board;
}
}
JButton[][] buttons = new JButton [10][10];
only creates an array of nulls, you have to initialize the buttons. and adding null to panel causes a NPE.
Just tried your code and noticed that you get NPE because you are just creating a buttons array but do not create buttons in it so your buttons array contains null values and when trying to add it in board it throws NPE.
Try something like this after creating array:
private static JPanel Board() {
JButton[][] buttons = new JButton [10][10];
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
buttons[i][j] = new JButton();
}
}
JPanel board = new JPanel(new GridLayout(11,11));
String[] rowlabels = {" ","A","B","C","D","E","F","G","H","I","J"};
String[] columnlabels = {" ","1","2","3","4","5","6","7","8","9","10"};
JTextField k;
// ---- your remaining code.
}

Categories

Resources