I am working on Conway's Game of Life for a school project. I am not looking for the code directly. I am looking to find out what is wrong with my code.
In Conway's Game of Life a cell goes from dead to alive if it has 3 alive neighbors. It stays alive if it has two or three alive neighbors. If none of those are true it is dead.
My LifeView class has a method that displays the cell simulation and afterwards displays how many alive cells are around the given point.
This is the output I am getting:
How many rows is your simulation?
5
How many columns is your simulation?
5
How many generations is your simulation?
3
xxxxx
xxxxx
xx0xx
xx0xx
xx0xx
00000
01110
02120
03230
02120
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
00000
00000
00000
00000
00000
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
00000
00000
00000
00000
00000
This is wrong because the second generation is supposed to be a horizontal line of live cells crossing the center of the first generation alive cells. Instead of crossing that center, all cells are turned dead. I am stumped as to why it doesn't work.
Main class:
package gameOfLife;
import java.util.Scanner;
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
Scanner numReader = new Scanner(System.in);
System.out.println("How many rows is your simulation?");
int rows = numReader.nextInt();
System.out.println("How many columns is your simulation?");
int columns = numReader.nextInt();
System.out.println("How many generations is your simulation?");
int generations = numReader.nextInt();
LifeModel model = new LifeModel(rows,columns);
LifeView life = new LifeView(model);
for(int i=0; i<generations; i++)
{
life.displayLife();
model.nextGeneration();
}
}
LifeView class:
package gameOfLife;
import java.util.Scanner;
public class LifeView {
private LifeModel model;
public LifeView(LifeModel model)
{
this.model = model;
}
public void displayLife()
{
for(int i=0; i < model.getWorld().length; i++)
{
for(int j=0; j < model.getWorld()[0].length; j++)
{
if(model.getWorld()[i][j])
{
System.out.print("0");
}
else
{
System.out.print("x");
}
}
System.out.println("");
}
System.out.println("");
for(int i=0; i < model.getWorld().length; i++)
{
for(int j=0; j < model.getWorld()[0].length; j++)
{
System.out.print(model.numLivingNeighbors(i,j));
}
System.out.println("");
}
System.out.println("");
System.out.println("");
}
}
LifeModel class:
package gameOfLife;
public class LifeModel
{
private boolean[][] world;
private int numRows;
private int numCols;
private boolean[][] tempWorld;
public LifeModel(int rows, int cols)
{
this.numRows=rows;
this.numCols=cols;
world = new boolean[rows][cols];
initWorld();
tempWorld = world;
}
private void initWorld()
{
boolean done = false;
while(!done)
{
int i = (int) (Math.random()*numRows);
int j = (int) (Math.random()*numCols);
if(j>0 && i>0 && i<numRows-1 && j<numCols-1)
{
/*
world[i-1][j-1] = true;
world[i-1][j] = true;
world[i-1][j+1] = true;
world[i][j+1] = true;
world[i+1][j] = true;
*/
world[i][j]=true;
world[i+1][j]=true;
world[i-1][j]=true;
done = true;
}
}
}
public void nextGeneration()
{
//tempWorld = new boolean[numRows+2][numCols+2];
int rows = world.length;
int columns = world[0].length;
for(int i=0; i < rows; i++)
{
for(int j = 0; j < columns; j++)
{
toggleCell(i,j);
}
}
world = tempWorld;
}
public void toggleCell(int r, int c)
{
int count = numLivingNeighbors(r,c);
if(!world[r][c] && count==3)
{
tempWorld[r][c] = true;
}
else if(world[r][c] && (count>=2 && count<=3))
{
tempWorld[r][c] = true;
}
else
{
tempWorld[r][c] = false;
}
}
public int numLivingNeighbors(int r, int c)
{
int count = 0;
boolean newCells[][] = world;
for(int i = -1; i<=1; i++)
{
for(int j = -1; j<=1; j++)
{
if(i!=0 || j!=0)
{
int row = r + i;
int column = c + j;
if(row>=0 && row < newCells.length && column>=0 && column<newCells[0].length && newCells[row][column])
{
count++;
}
}
}
}
return count;
}
public void userChange()
{
}
public boolean[][] getWorld()
{
return world;
}
}
Any help is GREATLY appreciated!
You just have a couple small issues with your LifeModel class.
In your constructor you set the tempWorld to reference the same array as the actual game world. This will cause any modifications to tempWorld to also affect the gameWorld.
public LifeModel(int rows, int cols)
{
this.numRows=rows;
this.numCols=cols;
world = new boolean[rows][cols];
initWorld();
//tempWorld = world; // You can remove this line.
}
Then in next generation you have the line "//tempWorld = new boolean[numRows+2][numCols+2];" commented out. You really do need to create a new temp array here so you aren't changing the game board as you read it. However, I'm not sure what the +2 is supposed to be, so I removed it. You should have:
public void nextGeneration()
{
tempWorld = new boolean[numRows][numCols]; // Keep it the same size
int rows = world.length;
int columns = world[0].length;
for(int i=0; i < rows; i++)
{
for(int j = 0; j < columns; j++)
{
toggleCell(i,j);
}
}
world = tempWorld;
}
After I made those changes it worked perfectly for me. I've included the full LifeModel class below that I used on my machine.
package gameOfLife;
public class LifeModel
{
private boolean[][] world;
private int numRows;
private int numCols;
private boolean[][] tempWorld;
public LifeModel(int rows, int cols)
{
this.numRows=rows;
this.numCols=cols;
world = new boolean[rows][cols];
initWorld();
}
private void initWorld()
{
boolean done = false;
while(!done)
{
int i = (int) (Math.random()*numRows);
int j = (int) (Math.random()*numCols);
if(j>0 && i>0 && i<numRows-1 && j<numCols-1)
{
/*
world[i-1][j-1] = true;
world[i-1][j] = true;
world[i-1][j+1] = true;
world[i][j+1] = true;
world[i+1][j] = true;
*/
world[i][j]=true;
world[i+1][j]=true;
world[i-1][j]=true;
done = true;
}
}
}
public void nextGeneration()
{
tempWorld = new boolean[numRows][numCols];
int rows = world.length;
int columns = world[0].length;
for(int i=0; i < rows; i++)
{
for(int j = 0; j < columns; j++)
{
toggleCell(i,j);
}
}
world = tempWorld;
}
public void toggleCell(int r, int c)
{
int count = numLivingNeighbors(r,c);
if(!world[r][c] && count==3)
{
tempWorld[r][c] = true;
}
else if(world[r][c] && (count>=2 && count<=3))
{
tempWorld[r][c] = true;
}
else
{
tempWorld[r][c] = false;
}
}
public int numLivingNeighbors(int r, int c)
{
int count = 0;
boolean newCells[][] = world;
for(int i = -1; i<=1; i++)
{
for(int j = -1; j<=1; j++)
{
if(i!=0 || j!=0)
{
int row = r + i;
int column = c + j;
if(row>=0 && row < newCells.length && column>=0 && column<newCells[0].length && newCells[row][column])
{
count++;
}
}
}
}
return count;
}
public void userChange()
{
}
public boolean[][] getWorld()
{
return world;
}
}
Check that numLivingNeighbors is returning the proper value for each cell in the world.
Also check the step for staying alive
Hey You have done a simple mistake in your code
public LifeModel(int rows, int cols)
{
this.numRows=rows;
this.numCols=cols;
world = new boolean[rows][cols];
initWorld();
tempWorld = world;
}
this is LifeModel constructor.
In this constructor you need to initialize tempworld also. You should not assign your world to tempworld.
After modification this block of code will become like this....
public LifeModel(int rows, int cols)
{
this.numRows=rows;
this.numCols=cols;
world = new boolean[rows][cols];
tempWorld = new boolean[rows][cols];
initWorld();
}
After this your output will be correct.
Related
3 questions in three days (I wish I could fix my problems alone) and today is still about my Sudoku project. I'm working on building a Sudoku game in Java and am working with other people who have programmed various parts of the project and right now we are trying to join together the Sudoku solver class with the JFrame class which makes it all pretty (Or it will at one point) but right now I'm having an issue with the way my 2D arrays are working (more like aren't). Below is the code where I call the solver and feed it into a lot of methods to do things:
public class FenetreGrille extends JFrame implements ActionListener{
public static int [][] NOMBRES_DEBUT;
public static int [][] GRILLE_MODIF = new int[9][9];
public int [][] GRILLE_FINALE = new int[9][9];
public static final int TAILLE = 9;
public static int TAILLECASE = 60;
public static int COTEGRILLE ;
public FenetreGrille(int [][] t){
NOMBRES_DEBUT = t;
GRILLE_MODIF = NOMBRES_DEBUT;
GRILLE_FINALE = NOMBRES_DEBUT;
COTEGRILLE = TAILLE * TAILLECASE;
SudokuBackTrack sbt = new SudokuBackTrack(GRILLE_FINALE);
// Here is the problem ^^^^^
// More code below that shouldn't be important...
}
And here is the SudokuBackTrack class:
public class SudokuBackTrack{
public static int[][] grille;
public static int[][] grilleResolu;
public static int[][] grillePos;
public static boolean[][] existeSurLigne = new boolean[9][9];
public static boolean[][] existeSurColonne = new boolean[9][9];
public static boolean[][] existeSurBloc = new boolean[9][9];
public final static int taille = 9;
public static ArrayList<Case> valParCase;
public SudokuBackTrack(int[][] t) {
grille = t;
grilleResolu = grille;
valParCase = listeValPossibles(grilleResolu);
tableauxExistence(grilleResolu);
backtracking(0, grilleResolu);
}
public static ArrayList<Case> listeValPossibles(int[][] temp) {
ArrayList<Case> t = new ArrayList<Case>();
for (int i=0; i<9; i++){
for (int j=0; j<9; j++){
if(temp[i][j] == 0) {
int pos = i*taille+j;
t.add(valeursPossibles(pos, temp));
}
}
}
Collections.sort(t);
return t;
}
public static Case valeursPossibles(int pos, int[][] t) {
int i = pos/9;
int j = pos%9;
int valPossibles = 9;
for(int s=1; s<=9; s++) {
if(!absentSurLigne(s, i, t) || !absentSurColonne(s, j, t) || !absentDansBloc(s, i, j, t)) {
valPossibles--;
}
}
Case a = new Case(pos, valPossibles);
return a;
}
public static boolean absentSurLigne(int k, int i, int[][] t) {
for (int j=0; j < 9; j++) {
if (t[i][j] == k) {
return false;
}
}
return true;
}
public static boolean absentSurColonne(int k, int j, int[][] t) {
for (int i=0; i < 9; i++) {
if (t[i][j] == k) {
return false;
}
}
return true;
}
public static boolean absentDansBloc(int k, int i, int j, int[][] t) {
int _i = i-(i%3); // ou encore : _i = 3*(i/3);
int _j = j-(j%3); // ou encore : _j = 3*(j/3);
for (i=_i; i < _i+3; i++) {
for (j=_j; j < _j+3; j++) {
if (t[i][j] == k) {
return false;
}
}
}
return true;
}
public static void tableauxExistence(int[][] t) {
for (int i=0; i < 9; i++) {
for (int j=0; j < 9; j++) {
existeSurLigne[i][j] = existeSurColonne[i][j] = existeSurBloc[i][j] = false;
}
}
int k;
for (int i=0; i < 9; i++) {
for (int j=0; j < 9; j++) {
if ((k = t[i][j]) != 0) {
existeSurLigne[i][k-1] = existeSurColonne[j][k-1] = existeSurBloc[3*(i/3)+(j/3)][k-1] = true;
}
}
}
}
public static boolean backtracking(int index, int[][] t) {
if(index == valParCase.size()) {
return true;
}
int i = (valParCase.get(index).position)/9;
int j = (valParCase.get(index).position)%9;
for(int k = 0; k < 9; k++) {
if(!existeSurLigne[i][k] && !existeSurColonne[j][k] && !existeSurBloc[3*(i/3)+(j/3)][k]){
// Ajoute k aux valeurs enregistrées
existeSurLigne[i][k] = existeSurColonne[j][k] = existeSurBloc[3*(i/3)+(j/3)][k] = true;
if(backtracking(index+1, t)){
// Ecrit le choix valide dans la grille
t[i][j] = k+1;
return true;
}
// Supprime k des valeurs enregistrées
existeSurLigne[i][k] = existeSurColonne[j][k] = existeSurBloc[3*(i/3)+(j/3)][k] = false;
}
}
t[i][j] = 0;
return false;
}
As mentioned above, my problem is that when I create an instance of my SudokuBackTrack class using the 2D Array GRILLE_FINALE, all my 2D Arrays NOMBRES_DEBUT, GRILLE_MODIF and GRILLE_FINALE become solved Sudoku grids whereas all I want is for GRILLE_FINALE to become the solved version, not all 3. I've tried debugging the codes but I haven't found anything and since it's a mixture of codes from different people, I don't know how they each created their parts. I've modified the attribute types and tried all sorts of fancy things but non worked and I'm out of ideas and mainly time... Thanks in advance and sorry for the huge question and code.
You're placing the same instance of the 2D array into NOMBRES_DEBUT, GRILLE_MODIF, GRILLE_FINALE. I think what you're looking to do is to place a copy NOMBRES_DEBUT into GRILLE_MODIF and GRILLE_FINALE.
They way you have it set up now, whever you make a modification to any of the 3 arrays, you will end up modifying all 3 because all 3 point to the same array in memory.
I am making a grid with the amounts determined by a scanner. I keep getting an error and I am not sure why. Here is the code for the grid that I am trying to make, I will also include the object and class for the maze/grid below.
public static void mazeSetup() {
System.out.println("How many rows and columns do you want? I would\n"
+ "suggest 10 minimum and 20 maximum.");
boolean mazeselect = true;
while(mazeselect) {
maze.columns = sc.nextInt();
maze.rows = maze.columns;
if (maze.rows > 30 || maze.rows < 10) {
System.out.println("Make sure that you make it within 10-30 rows.");
} else {
mazeselect = false;
}
}
mazeBuild();
}
public static void mazeBuild() {
for(int x = 0; x < maze.rows; x++) {
for(int y = 0; y < maze.columns; y++) {
maze.maze[x][y]= ".";
System.out.print(maze.maze[x][y]);
}
System.out.println();
}
characterPlacement();
}
I also have the object here:
static Maze maze = new Maze(null,0,0,0,0);
and the class with construtors for the maze/grid.
public class Maze {
String maze[][];
int rows;
int columns;
int xStart;
int yStart;
public Maze(String xMaze[][], int xRows, int xColumns, int xxStart, int xyStart) {
maze = xMaze;
rows = xRows;
columns = xColumns;
xStart = xxStart;
yStart = xyStart;
}
public String[][] maze() {
return maze;
}
public int rows() {
return rows;
}
public int columns() {
return columns;
}
public int xStart() {
return xStart;
}
public int yStart() {
return yStart;
}
}
Any help would be greatly appreciated. Thanks a lot! :D
Note: No errors occur until ran in console.
your String maze[][] is null because of this:
static Maze maze = new Maze(null,0,0,0,0); // notice that null
And you're trying to put values in it upon calling mazeBuild(). You should initialize it or pass an array instead of null. You can do this at the start of mazeBuild()
public static void mazeBuild() {
maze.maze = new String[maze.rows][maze.columns]; // <-- this one!
for(int x = 0; x < maze.rows; x++) { // <-- this loop tries to
for(int y = 0; y < maze.columns; y++) { // put values in your
maze.maze[x][y]= "."; // maze.maze (2D String array)
System.out.print(maze.maze[x][y]);
}
System.out.println();
}
You can also do this in exchange to the line of code I've added.
String[][] mazeArray = new String[maze.rows][maze.columns];
maze = new Maze(mazeArray, maze.rows, maze.columns, 0, 0);
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!
iam making a simple game called ships, i made a 2D array (10x10), i have a problem with adding a "ship" to array ( the point of the game is to find a ship in an array), im completely stucked so i am looking for a help, sorry for mistakes and etc, but this is my first post and i am just begginer. Thanks!
Here is MAIN
public class Play
{
public static void main(String[] args)
{
Grid grid = new Grid(10);
grid.fillArr('o');
grid.printArr();
Objects ships = new Object(0);
ship.getShip('X');
}
}
Object(SHIP) CLASS
public class Objects
{
//FIELDS
private int ship;
//CONSTRUCTORS
public Objects(int ship)
{
this.ship = ship;
}
//METHODS
public int getShip()
{
return ship;
}
public void getLod(char c)
{
}
}
Here is the Grid Class
public class Grid
{
//FIELDS
char[][] arr;
private Objects[] ship;
private int numofShips;
//CONSTRUCTORS
public Grid(int a)
{
arr = new char [a][a];
ship = new Objects[10];
}
//METHODS
public void printArr()
{
for(int q = 0; q < arr.length; q = q + 1)
{
for(int w = 0; w < arr.length; w++)
{
System.out.print(arr[q][w] + " ");
}
System.out.print("\n");
}
}
public void fillArr(char c)
{
for(int q = 0; q < arr.length; q++)
{
for(int w = 0; w < arr.length; w++)
{
arr[q][w] = c;
}
}
}
public void reloadGrid()
{
//Draw base grid
for(int r = 0; r < arr.length; r++)
for(int c = 0; c < arr[r].length; c++)
arr[r][c] = 'o';
//Draw ship <-----THIS IS A PROBLEM...//////////////////////////////
for(int i = 0; i < numofShips; i++)
{
}
}
public void addToGrid(Objects p)
{
ship[numofShips] = p;
numofShips++;
}
}
Sorry for all mistakes in the code.
I'm trying to solve the problem of positioning N queens on NxN board without row, column and diagonal conflicts. I use an algorithm with minimizing the conflicts. Firstly, on each column randomly a queen is positioned. After that, of all conflict queens randomly one is chosen and for her column are calculated the conflicts of each possible position. Then, the queen moves to the best position with min number of conflicts. It works, but it runs extremely slow. My goal is to make it run fast for 10000 queens. Would you, please, suggest me some improvements or maybe notice some mistakes in my logic?
Here is my code:
public class Queen {
int column;
int row;
int d1;
int d2;
public Queen(int column, int row, int d1, int d2) {
super();
this.column = column;
this.row = row;
this.d1 = d1;
this.d2 = d2;
}
#Override
public String toString() {
return "Queen [column=" + column + ", row=" + row + ", d1=" + d1
+ ", d2=" + d2 + "]";
}
#Override
public boolean equals(Object obj) {
return ((Queen)obj).column == this.column && ((Queen)obj).row == this.row;
}
}
And:
import java.util.HashSet;
import java.util.Random;
public class SolveQueens {
public static boolean printBoard = false;
public static int N = 100;
public static int maxSteps = 2000000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static HashSet<Queen> q = new HashSet<Queen>();
public static HashSet rowConfl[] = new HashSet[N];
public static HashSet d1Confl[] = new HashSet[2*N - 1];
public static HashSet d2Confl[] = new HashSet[2*N - 1];
public static void init () {
int r;
rowConfl = new HashSet[N];
d1Confl = new HashSet[2*N - 1];
d2Confl = new HashSet[2*N - 1];
for (int i = 0; i < N; i++) {
r = random.nextInt(N);
queens[i] = r;
Queen k = new Queen(i, r, i + r, N - 1 + i - r);
q.add(k);
if (rowConfl[k.row] == null) {
rowConfl[k.row] = new HashSet<Queen>();
}
if (d1Confl[k.d1] == null) {
d1Confl[k.d1] = new HashSet<Queen>();
}
if (d2Confl[k.d2] == null) {
d2Confl[k.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[k.row]).add(k);
((HashSet<Queen>)d1Confl[k.d1]).add(k);
((HashSet<Queen>)d2Confl[k.d2]).add(k);
}
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "♕ " : "◻◻◻ ");
}
System.out.println();
}
System.out.println();
}
public static boolean checkItLinear() {
Queen r = choseConflictQueen();
if (r == null) {
return true;
}
Queen newQ = findNewBestPosition(r);
q.remove(r);
q.add(newQ);
rowConfl[r.row].remove(r);
d1Confl[r.d1].remove(r);
d2Confl[r.d2].remove(r);
if (rowConfl[newQ.row] == null) {
rowConfl[newQ.row] = new HashSet<Queen>();
}
if (d1Confl[newQ.d1] == null) {
d1Confl[newQ.d1] = new HashSet<Queen>();
}
if (d2Confl[newQ.d2] == null) {
d2Confl[newQ.d2] = new HashSet<Queen>();
}
((HashSet<Queen>)rowConfl[newQ.row]).add(newQ);
((HashSet<Queen>)d1Confl[newQ.d1]).add(newQ);
((HashSet<Queen>)d2Confl[newQ.d2]).add(newQ);
queens[r.column] = newQ.row;
return false;
}
public static Queen choseConflictQueen () {
HashSet<Queen> conflictSet = new HashSet<Queen>();
boolean hasConflicts = false;
for (int i = 0; i < 2*N - 1; i++) {
if (i < N && rowConfl[i] != null) {
hasConflicts = hasConflicts || rowConfl[i].size() > 1;
conflictSet.addAll(rowConfl[i]);
}
if (d1Confl[i] != null) {
hasConflicts = hasConflicts || d1Confl[i].size() > 1;
conflictSet.addAll(d1Confl[i]);
}
if (d2Confl[i] != null) {
hasConflicts = hasConflicts || d2Confl[i].size() > 1;
conflictSet.addAll(d2Confl[i]);
}
}
if (hasConflicts) {
int c = random.nextInt(conflictSet.size());
return (Queen) conflictSet.toArray()[c];
}
return null;
}
public static Queen findNewBestPosition(Queen old) {
int[] row = new int[N];
int min = Integer.MAX_VALUE;
int minInd = old.row;
for (int i = 0; i < N; i++) {
if (rowConfl[i] != null) {
row[i] = rowConfl[i].size();
}
if (d1Confl[old.column + i] != null) {
row[i] += d1Confl[old.column + i].size();
}
if (d2Confl[N - 1 + old.column - i] != null) {
row[i] += d2Confl[N - 1 + old.column - i].size();
}
if (i == old.row) {
row[i] = row[i] - 3;
}
if (row[i] <= min && i != minInd) {
min = row[i];
minInd = i;
}
}
return new Queen(old.column, minInd, old.column + minInd, N - 1 + old.column - minInd);
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(!checkItLinear()) {
if (++steps > maxSteps) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(printBoard){
print();
}
}
}
Edit:
Here is my a-little-bit-optimized solution with removing some unused objects and putting the queens on diagonal positions when initializing.
import java.util.Random;
import java.util.Vector;
public class SolveQueens {
public static boolean PRINT_BOARD = true;
public static int N = 10;
public static int MAX_STEPS = 5000;
public static int[] queens = new int[N];
public static Random random = new Random();
public static int[] rowConfl = new int[N];
public static int[] d1Confl = new int[2*N - 1];
public static int[] d2Confl = new int[2*N - 1];
public static Vector<Integer> conflicts = new Vector<Integer>();
public static void init () {
random = new Random();
for (int i = 0; i < N; i++) {
queens[i] = i;
}
}
public static int getD1Pos (int col, int row) {
return col + row;
}
public static int getD2Pos (int col, int row) {
return N - 1 + col - row;
}
public static void print () {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(queens[i] == j ? "Q " : "* ");
}
System.out.println();
}
System.out.println();
}
public static boolean hasConflicts() {
generateConflicts();
if (conflicts.isEmpty()) {
return false;
}
int r = random.nextInt(conflicts.size());
int conflQueenCol = conflicts.get(r);
int currentRow = queens[conflQueenCol];
int bestRow = currentRow;
int minConfl = getConflicts(conflQueenCol, queens[conflQueenCol]) - 3;
int tempConflCount;
for (int i = 0; i < N ; i++) {
tempConflCount = getConflicts(conflQueenCol, i);
if (i != currentRow && tempConflCount <= minConfl) {
minConfl = tempConflCount;
bestRow = i;
}
}
queens[conflQueenCol] = bestRow;
return true;
}
public static void generateConflicts () {
conflicts = new Vector<Integer>();
rowConfl = new int[N];
d1Confl = new int[2*N - 1];
d2Confl = new int[2*N - 1];
for (int i = 0; i < N; i++) {
int r = queens[i];
rowConfl[r]++;
d1Confl[getD1Pos(i, r)]++;
d2Confl[getD2Pos(i, r)]++;
}
for (int i = 0; i < N; i++) {
int conflictsCount = getConflicts(i, queens[i]) - 3;
if (conflictsCount > 0) {
conflicts.add(i);
}
}
}
public static int getConflicts(int col, int row) {
return rowConfl[row] + d1Confl[getD1Pos(col, row)] + d2Confl[getD2Pos(col, row)];
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
init();
int steps = 0;
while(hasConflicts()) {
if (++steps > MAX_STEPS) {
init();
steps = 0;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Done for " + (endTime - startTime) + "ms\n");
if(PRINT_BOARD){
print();
}
}
}
Comments would have been helpful :)
Rather than recreating your conflict set and your "worst conflict" queen everything, could you create it once, and then just update the changed rows/columns?
EDIT 0:
I tried playing around with your code a bit. Since the code is randomized, it's hard to find out if a change is good or not, since you might start with a good initial state or a crappy one. I tried making 10 runs with 10 queens, and got wildly different answers, but results are below.
I psuedo-profiled to see which statements were being executed the most, and it turns out the inner loop statements in chooseConflictQueen are executed the most. I tried inserting a break to pull the first conflict queen if found, but it didn't seem to help much.
Grouping only runs that took more than a second:
I realize I only have 10 runs, which is not really enough to be statistically valid, but hey.
So adding breaks didn't seem to help. I think a constructive solution will likely be faster, but randomness will again make it harder to check.
Your approach is good : Local search algorithm with minimum-conflicts constraint. I would suggest try improving your initial state. Instead of randomly placing all queens, 1 per column, try to place them so that you minimize the number of conflicts. An example would be to try placing you next queen based on the position of the previous one ... or maybe position of previous two ... Then you local search will have less problematic columns to deal with.
If you randomly select, you could be selecting the same state as a previous state. Theoretically, you might never find a solution even if there is one.
I think you woud be better to iterate normally through the states.
Also, are you sure boards other than 8x8 are solvable?
By inspection, 2x2 is not, 3x3 is not, 4x4 is not.