Changing a program to an object oriented design - java

I need help rearranging a program I have to use classes constructors methods...so on. It's a game of battleships. I'm not too clear on what classes and constructors and methods I'd need.
import java.util.Scanner;
public class BattleshipLab {
Scanner input = new Scanner(System.in);
public static final boolean DEBUG = false;
public static void breakln() {
System.out.println("_____________________________________");
System.out.println("");
}
public static void createBoard(String[][] board) {
for (String[] board1 : board) {
for (int c = 0; c < board[0].length; c++) {
board1[c] = "~";
}
}
}
public static void showBoard(String[][] board) {
breakln();
for (String[] board1 : board) {
if (DEBUG == true) {
for (int c = 0; c < board[0].length; c++) {
System.out.print(" " + board1[c]);
}
System.out.println("");
} else {
for (int c = 0; c < board[0].length; c++) {
if (board1[c].equals("S")) {
System.out.print(" " + "~");
} else {
System.out.print(" " + board1[c]);
}
}
System.out.println("");
}
}
breakln();
}
public static void createShip(String[][] board, int size) {
if (Math.random() < 0.5) {
int col = (int) (Math.random() * 5);
int row = (int) (Math.random() * 7);
for (int i = 0; i < size; i++) {
board[row][col + i] = "S";
}
} else {
int col = (int) (Math.random() * 7);
int row = (int) (Math.random() * 5);
for (int i = 0; i < size; i++) {
board[row + i][col] = "S";
}
}
}
public static int userFire(String[][] board, int hits, int torps) {
Scanner input = new Scanner(System.in);
int row, col;
System.out.println("You have: " + torps + " torpedos left!");
System.out.println("Select a row to fire in: ");
row = input.nextInt();
while (row > 8 || row < 1) // Error checking for row
{
System.out.println("Enter a valid row (1 -> 8)");
row = input.nextInt();
}
System.out.println("Select a column to fire in: ");
col = input.nextInt();
while (col > 8 || col < 1) // Error checking for column
{
System.out.println("Enter a valid col (1 -> 8)");
col = input.nextInt();
}
if (board[row - 1][col - 1].equals("S")) {
hits++;
System.out.println("~~~~~~~ HIT ~~~~~~~");
board[row - 1][col - 1] = "!";
} else {
System.out.println("~~~~~~~ MISS ~~~~~~~");
board[row - 1][col - 1] = "M";
}
return hits;
}
public static void finall(int hits, int torps) {
if (hits < 4) {
System.out.println("Sorry, but you lost because you didn't sink the ship.");
}
if (torps < 1) {
System.out.println("You have lost all your torpedos");
} else if (hits >= 4) {
System.out.println("You have beaten the game battleship, Thanks for playing!");
}
System.out.println("Good game, well played!");
}
public static void main(String[] arg) {
String[][] board = new String[8][8];
createBoard(board);
createShip(board, 4);
int torps = 15;
int hits = 0;
/// Starting real stuff
while (torps > 0 && hits < 4) {
showBoard(board);
hits = userFire(board, hits, torps);
torps--;
}
finall(hits, torps);
}
}

Welcome to Stack Overflow, Noah!
First a couple of remarks:
This kind of question is usually considered as unclear what you are asking and/or too broad, so expect downvotes and a closing of your question. You should really read up on how to ask a good question.
If you are struggling with your code design, it is often a good idea to take a pen and some paper and draft a rough outline there. For your specific task of turning an existing program into OOP, a class diagram would be a really good starting point.
Now that that's out of the way, I'll give you a bit of a notch in the right direction, seeing that you are new to Stack Overflow.
In your code, we can find two interesting methods:
createBoard
createShip
Notice how they both start with create? This is a very, very strong hint that it would probably a good idea to write classes for those two objects at least:
Board
Ship
When it comes to methods, your current code gives us some pointers again. While I'm not sure about firing torpedos and the like, it looks as if you might want to at least turn showBoard() into a method show of your Board class.
Now, what other classes and methods you need really depends on the rules of that game (I'm not familiar with it), what features you want it to have and, of course, how you want to implement it. Possible other classes could be Torpedo but maybe those will just be a primitive type member of your Ship class. Another candidate could be something like Tile, the basic building block of your Board?
You'll have to take it from here, really. The Java Lessons on Classes and Objects will be useful.
I hope this gets you started.

Okay so when you're doing OOP, before you begin to code you should analyze your problem and break it down. What parts make up the game, what are their functions and attributes, how do these parts interact with each other? I'll help you start, consider making board and cell classes.
In OOP you like Java, you need to create a new instance of the object(class) that you define. So let's say you created a class:
class Shape {
int length, width;
String color ="";
}
To use Shape in main you create a new instance of it
class Driver {
public static void main (String [] args){
//First you specify the type as your object name, then name the variable and lastly, you do new object.
Shape myShape = new Shape ();
}
}

Related

Not sure where to start with JavaFX minesweeper GUI

I recently started a minesweeper project in Java. I planned to first develop the game mechanics while using the Console as output, but now I want to develop a GUI using the code I have. The problem is I have very little experience in GUI design.
One of my previous experiences in JavaFX was designing a Chess game. For the chess board, I created a 2D ImageView grid in the fxml. The images each ImageView would display would change as the pieces moved.
For my minesweeper game, I've implemented a variably sized board so I can't just create another ImageView 2D array again. I'm wondering if there is something in JavaFX that I don't know about that could help me approach this issue.
I would just like to know a good way to display the board of tiles itself. All the other GUI aspects of the game, I could figure out later.
The best thing that I could think of right now with what I know about JavaFX is that I can create a 2D ImageView array with the max size of the board. I could set all the height and width of the unused tiles to 0. However, even though this is the best idea that I have, I feel that it is a very bad way to approach the problem.
Here are the fields of the Board object
public class Board {
public final static int EASY_SIZE = 8;
public final static int MED_SIZE = 12;
public final static int HARD_SIZE = 15;
public final static int MIN_SIZE = 5;
public final static int MAX_SIZE = 26;
public int size;
protected int mine_count;
public boolean ended = false;
protected Tile[][] board;
The board output to the Console. The board.display() function:
public void display() {
System.out.println();
System.out.println(mine_count + " remaining mines");
for (int i = 0; i< 10 && i < size; i++) {
System.out.print(" _");
}
for(int i = 10; i < size; i++) {
System.out.print(" __");
}
System.out.println();
// For each row
for (int r = 0; r < size; r++) {
// For each col
for (int c = 0; c < size && c < 10; c++) {
// if the tile is flagged
if (board[r][c].flag == true) {
System.out.print("|F");
}
// else if the tile has not been clicked
else if (board[r][c].clicked == false) {
System.out.print("|#");
}
// else if mine clicked
else if (board[r][c].mine == true) {
System.out.print("|*");
}
// else if zero adj_mines
else if (board[r][c].adj_mines == 0) {
System.out.print("|_");
}
// else show number
else {
System.out.print("|" + board[r][c].adj_mines);
}
}
for (int c = 10; c < size; c++) {
// if the tile is flagged
if (board[r][c].flag == true) {
System.out.print("|F_");
}
// else if the tile has not been clicked
else if (board[r][c].clicked == false) {
System.out.print("|#_");
}
// else if mine clicked
else if (board[r][c].mine == true) {
System.out.print("|*_");
}
// else if zero adj_mines
else if (board[r][c].adj_mines == 0) {
System.out.print("|__");
}
// else show number
else {
System.out.print("|" + board[r][c].adj_mines + "_");
}
}
System.out.println("| " + r);
}
for (int i = 0; i < size; i++) {
System.out.print(" " + i);
}
System.out.println();
}
...
Since my question is regarding what sort of JavaFX layout I could use to approach the problem, I started looking more into my options. In my previous experiences in JavaFX, I designed all of the GUI elements in an FXML file, but I realize that since I'm not certain about the size of the board on bootup, I should not be doing this.
Instead, I'm planning to use a GridPane layout and I will change my Tile class to extend Button to set an EventHandler on left and right mouse clicks. If anyone has insight on why this might not work, I would love to hear it, but otherwise, I'll report back if this works on not!

How to compress primitive integer data for an array?

I am teaching myself java using the cs106a course from Stanford.
Currently I am on chapter 10 of the book "The Art and Science of Java".
The problem is to write a 3x3 Magic Square.
The exercise:
You have to write a 3x3 array
Each side of the array(Magic Square) has to equal 15
The problem:
The program I wrote works, the assignment is complete, this question is for self learning. As a beginner I would like to improve the method SumOfSides() and make it smaller and more efficient. I tried iterating the array in this method but still have issues. Is there a way to make it more efficient?
public void run() {
//set the font
setFont("Helvetica-40");
//fill the array
fillArray();
//sum up all sides
SumOfSides();
//check if all of the sides in the magic square array equal 15:
checkSides(mSqr);
//I used this for debugging purposes only:
//showSides();
}
//for debugging purposes:
public void showSides() {
println(sumRight0);
println(sumRight1);
println(sumRight2);
println(sumBottom0);
println(sumBottom1);
println(sumBottom2);
println(sumDiagonalUp);
println(sumDiagonalDown);
}
public void SumOfSides() {
sumRight0 = mSqr[0][0] + mSqr[0][1] + mSqr[0][2];
sumRight1 = mSqr[1][0] + mSqr[1][1] + mSqr[1][2];
sumRight2 = mSqr[2][0] + mSqr[2][1] + mSqr[2][2];
sumBottom0 =mSqr[0][0] + mSqr[1][0] + mSqr[2][0];
sumBottom1 =mSqr[0][1] + mSqr[1][1] + mSqr[2][1];
sumBottom2 =mSqr[0][2] + mSqr[1][2] + mSqr[2][2];
sumDiagonalUp = mSqr[2][0] + mSqr[1][1]+ mSqr[0][2];
sumDiagonalDown = mSqr[0][0] + mSqr[1][1] + mSqr[2][2];
}
/*This predicate method checks if the sides
of the array add up to 15: */
public boolean checkSides(int[][] myArray) {
if (sumRight0 ==15 && sumRight1 ==15&& sumRight2==15 && sumBottom0==15&& sumBottom1==15&&
sumBottom2==15&& sumDiagonalUp==15&&sumDiagonalDown==15) {
println("True, this is a Magic Square");
return true;
} else {
println("False, the sides do not equal 15");
return false;
}
}
public void fillArray() {
int num =0;
for(int row=0; row <3; row++) {
for (int col=0; col<3; col++) {
num=readInt("");
mSqr[row][col]=num;
}
}
/*Test array values here to see
* if they were entered correctly.
*/
//println(mSqr[1][2]); //should be 6
//println(mSqr[2][0]); //should be 7
}
//instance variables:
int[][] mSqr= new int[3][3];
List<List<Integer>> new1 = new ArrayList<>();
private int sumRight0;
private int sumRight1;
private int sumRight2;
private int sumBottom0;
private int sumBottom1;
private int sumBottom2;
private int sumDiagonalUp;
private int sumDiagonalDown;
}
Perhaps the only thing is readability. You could take the values and move them into more readable variables:
int topLeft = mSqr[0][0];
int topMid = mSqr[0][1];
...
int sumLeft = topLeft + midLeft + bottomLeft;
int sumRight = topRight = midRight + bottomRight;
...
To address your concern of making it smaller, I would argue that converting the sums into loops, as you mentioned, is certainly not worth it in the case that you are doing 6 sums of 3 values each. Furthermore, each term of each sum is common to either one or two other sums, which does not provide much overlap. If you were performing larger sums (larger in number of terms in the sum, not the total value), then perhaps it would be worth it on a readability/SLOC argument.
Suppose you did want to do a loop still though, you could do something like
sumLeft = 0;
sumRight = 0;
sumTop = 0;
sumBottom = 0;
sumDiagonalUp = 0;
sumDiagonalDown = 0;
for(int i = 0; i < mSqr.length; i++) {
for(int j = 0; j < mSqr[i].length; j++) {
if (i == 0) {
sumLeft += mSqr[i][j];
}
if (i == mSqr.length - 1) {
sumRight += mSqr[i][j];
}
if (j == 0) {
sumTop += mSqr[i][j];
}
if (j == mSqr[i].length) {
sumBottom += mSqr[i][j];
}
if (i == j) {
sumDiagonalDown += mSqr[i][j];
}
if (i + j == mSqr.length - 1) {
sumDiagonalUp += mSqr[i][j];
}
}
}
The loops only provide benefit on large magic squares.
Also, I am confused by your description contrasted with your implementation. It seems you are summing each row and column, and the two diagonals of the square, as opposed to the 4 sides and the diagonals.

Minmax for ConnectFour

I am trying to implement a minmax algorithm to create an AI for connect four. I'm having quite a bit of trouble with it though as I feel like I have overcomplicated things (and it doesn't work properly), perhaps someone here can help. I'm going to post my code first and then the issue I'm having with it below.
This is the initial call to the minmax algorithm
public int getColumnForMove(ConnectFour game)
{
game.minimax(2, game.getCurrentPlayer(), game);
int column = game.getBestMove();
return column;
}
This is the initial minimax method (it is inside the ConnectFour class which is not where the initial method is called from that is in a separate AI class) that is called and a subclass that holds each column the user moves into as well as the min/max'ed score if it moves into that column.
class ColumnsAndScores
{
int column;
int score;
ColumnsAndScores(int column, int score)
{
this.column = column;
this.score = score;
}
}
List<ColumnsAndScores> cas = new ArrayList<>();
public void minimax(int depth, int turn, ConnectFour game)
{
cas = new ArrayList<>();
minimaxHelper(depth, depth, turn, game);
}
The following are methods that get the min or max score from each set of possible moves:
public int getMax(List<Integer> list)
{
int max = Integer.MIN_VALUE;
int index = -1;
for (int i = 0; i < list.size(); i++)
{
if (list.get(i) > max)
{
max = list.get(i);
index = i;
}
}
return list.get(index);
}
public int getMin(List<Integer> list)
{
int min = Integer.MAX_VALUE;
int index = -1;
for (int i = 0; i < list.size(); i++)
{
if (list.get(i) < min)
{
min = list.get(i);
index = i;
}
}
return list.get(index);
}
This is the actual minimax method (it has a bunch of commented out code that shows it should return a range of values depending on how good the board is if its not a clear cut win or loss but right now I'm just trying to have it make decisions based on a win or loss (if none of that happens in the requested depth it makes a random move)).
public int minimaxHelper(int originalDepth, int depth, int turn, ConnectFour game)
{
//holds future game states
ConnectFour futureGameState;
//holds the current scores
List<Integer> scores = new ArrayList<>();
//if (not at the lowest depth)
if (depth !=0)
{
if (checkForWin(turn))
{
//return Integer.MAX_VALUE or Integer.MIN_VALUE respectively based on who's turn it is
return (turn % 2 == 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
//recursively call getColumnForMove(depth--, otherTurn) for each column if the column isnt full
for (int i = 1; i <= ConnectFour.NUM_OF_COLUMNS; i++)
{
futureGameState = new ConnectFour();
futureGameState.setCurrentGameState(game.getCurrentGameState());
futureGameState.setCurrentPlayer(game.getCurrentPlayer());
if (futureGameState.isValidMove(i))
{
futureGameState.makeMove(i);
futureGameState.switchPlayer();
scores.add(minimaxHelper(originalDepth, depth - 1, futureGameState.getCurrentPlayer(), futureGameState));
}
else //if move isnt valid return the worst possible value so this column doesnt get chosen
{
return (turn % 2 == 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
if (depth == originalDepth)
{
ColumnsAndScores newScore;
if (turn % 2 == 0)
newScore = new ColumnsAndScores(i, getMax(scores));
else
newScore = new ColumnsAndScores(i, getMin(scores));
cas.add(newScore);
}
}
if (turn % 2 == 0)
return getMax(scores);
else
return getMin(scores);
}
else
{
if (checkForWin(turn))
{
//return Integer.MAX_VALUE or Integer.MIN_VALUE respectively based on who's turn it is
return (turn % 2 == 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
else
{
return 0;
}
//else
//if 3 in a row with 2 open spaces that have pieces under those spaces
//return 100
//else if 3 in a row with 1 open space that has a piece under that space
//return 80;
//else if 3 in a row
//return 60;
//else if 2 in a row
//return 40
//else
//return 0
}
}
and finally this is a method that is called by the AI to get the best move from the list that minimax added the ColumnAndScores too.
public int getBestMove()
{
int highestScore = Integer.MIN_VALUE;
int best = -1;
for (int i = 0; i < cas.size(); ++i) {
if (highestScore < cas.get(i).score) {
highestScore = cas.get(i).score;
best = i;
}
}
if (highestScore == 0)
return 1 + ((int) (Math.random() * 7));
else
return best;
}
While I believe there are a couple of logic errors the thing I am having the most difficulty with at the moment is that when I dofutureGameState = new ConnectFour();
futureGameState.setCurrentGameState(game.getCurrentGameState());
This should put it into a separate instance so that when I then make a move it should only last for that branch of the tree and not corrupt the actual game being played but that isn't the case. It is changing the actual state of the game being passed in.
The issue is most probably caused by the implementation of ConnectFour, something like
private int[][] state;
public void setCurrentGameState(int[][] newState) {
this.state = newState;
}
That's okay, but causes your "copy" of the game state to actually reference the same int[][] state, thus any modifications to it will apply to both states. What you want is
public class ConnectFour implements Cloneable<ConnectFour> {
private static final int NUM_ROWS = 6;
private static final int NUM_COLS = 7;
private int[][] state = new int[NUM_ROWS][NUM_COLS];
// ...
public ConnectFour clone() {
int[][] stateCopy = new int[NUM_ROWS][NUM_COLS];
for (int i = 0; i < NUM_ROWS; i++)
for (int j = 0; j < NUM_COLS; j++)
stateCopy[i][j] = this.state[i][j];
ConnectFour cloned = new ConnectFour();
cloned.setCurrentGameState(stateCopy);
// copy other fields over to cloned
return cloned;
}
}
I'm just going to address one issue. You should try not to have too many per question, and include the code relevant to your question, such as your ConnectFour class here.
If you want to make a copy of the board you can modify without changing the original, you need to make a deep copy, not a copy of the reference. To make a shallow copy of your house, you make a copy of your house key. If you give it to someone, you shouldn't be surprised to see changes when you get home. To make a deep copy of your house, you get a second lot and build a new house from blueprints and photos of your house. If you give a key to the new house to someone, he/she might not notice the difference immediately, but any changes shouldn't affect you directly, and changes you make won't affect the recipient.
"Deep copy" is actually ambiguous because your object may contain object members that have object members. When you make a deep copy, you have to decide whether to make deep copies or shallow copies of any member objects. If your ConnectFour class contains an ArrayList of Move objects, each of which is a wrapper for an int representing a column, you have 3 choices:
You can copy a reference to the ArrayList.
You can make a new ArrayList with references to the same set of moves.
You can make a new ArrayList with references to copies of the moves.
Anyway, my guess is that you don't yet have nested member objects, so your deep copy method can look something like the following:
public class ConnectFour{
private int[][] board = new int[6][7];
public setCurrentGameState(int[][] state){
for(int i = 0; i<6; i++)
for(int j=0; j<7; j++)
board[i][j] = state[i][j];
}
...

Array not returning strings

This is assessed work so please don't give the answer, just advice!
I'm trying to get my program to return the strings pass, compensation pass or fail depending on the values inputted by the user. However, it's not returning the values and I'm receiving an error for 'weighting'. Earlier it was working, however not in a suitable way because it wouldn't always return the correct before results. I added the array because i think that's what is needed, but now I'm just getting an error. Cheers.
enter code here
public class MarkCalculator {
static int[] marks = new int[12];
static int[] weighing = new int[6];
// public static Scanner keyboard = new Scanner(System.in);
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
int weighting;
int coursework;
int exammark;
for (int i = 0; i < marks.length / 2; i++) {
System.out.println("Please enter course work weighting");
weighting = kb.nextInt();
weighing[i] = weighting;
}
for (int i = 0; i < marks.length / 2; i++) {
System.out.println("Please enter course work mark ");
coursework = kb.nextInt();
marks[i] = coursework;
}
for (int i = 0; i < marks.length / 2; i++) {
System.out.println("Please enter exam mark ");
exammark = kb.nextInt();
marks[i + 6] = exammark;
}
System.out.println("Calculating Marks");
MarkCalculator mc = new MarkCalculator();
String[] results = mc.computeMarks(marks, weighing);
for (String result : results) {
System.out.println("Results are " + result);
}
}
public String[] computeMarks(int[] marks, int[] weighing) {
int[] formula = new int[12];
String[] results = new String[weighing.length];
for (int i = 0; i < weighing.length; i++) {
int exam = marks[i];
int cw = marks[i+weighing.length];
int weight = weighing[i];
formula [i]= ((cw + weight) + (exam * (100 - weight)) / 100);
if ((formula[i]<=39) && (formula[i] > 35)) {
results[i] = "COMPENSATION PASS";}
else if (formula[i] >= 40) {
results[i] = "PASS";
}
else {
results[i] = "FAIL";
}
}
return results;
}
public static void computeResult (int[] coursework, int[] exammark)
{
computeResult(coursework,exammark);
}
}
Was posted as comment:
You could separate the marks into two arrays which will be easier to debug? Also it seems like you might be going over the array index for weightings on this line
for (int i = 0; i < marks.length;i++)
{
int exam = marks[i];
int cw = marks[i];
int weight = weighing[i]; // Error is here
//...
}
Because "weighing" has a range 0-5 and you are cycling through to 0-11 (with the marks array)
weighting and marks are different length arrays, yet you are doing the loop
for (int i = 0; i < marks.length; i++)
which will go out of bounds for weighting when i > 5.
It looks like you need to do something like this:
for (int i = 0; i < weighting.length; i++) {
int cw = marks[i];
int exam = marks[i+weighting.length];
int weight = weighing[i];
But that will depend on how you are storing the marks for the cw and exam in the marks array. I would recommend creating separate arrays for cw and exam as these are different items and will make things a lot easier to read and debug for yourself.
As you've asked for tips to improve your program as well, without specific code, then I would consider doing the following:
1) Have separate arrays for exam and cw marks. You're making it hard for yourself to debug your program by concatenating them together and this is also the source of your error.
2) Assuming that you always have the same number of exams as you do cw elements then I would consider having a class variable in MarkCalculator that stores the number of tests. Something like this:
private static int NUM_TESTS.
This way you can initialise arrays like this:
private static int[] examMarks = new int[NUM_TESTS]
and you can do the looping in computeMarks like this:
for (int i = 0; i < NUM_TESTS; i++)
This way if you decide you want more tests you only have to update the code in one place. It would also be easy to change your program so that the user could define how many tests should be calculated.
3) Where you have:
weighting = kb.nextInt();
weightings[i] = weighting;
replace it with:
weightings[i] = kb.nextInt();
as the variable weighting only seem to be used in this place and is therefore unnecessary. This will result in fewer operations the program has to perform and reduces the amount of code on the screen. In practice the compiler will likely remove this redundant variable, but it is good practice to think about how many operations you are performing and which of them aren't necessary.
4) It's better practice to explicitly set access modifiers on fields in a class. So you should have:
'private static int[] weightings = new int[NUM_TESTS];`
If you want to access it from another class you would then typically specify a getter method like so:
public int[] getWeightings() { return weightings; }
5) This is less important, but I would move main to the bottom of the class. In Java it's more typical to see the classes fields first, then the constructor, then public methods, then private methods and have the main at the bottom. In large projects it helps keeping to good style as it makes the code easier to read and understand.
These reference might help you learn more:
Java Coding Style Guide
Oracle Tutorial on access-modifiers
You will encounter an error as "Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6"
This is because in method computeMarks. The length of marks (int[]) is 12.
and you just declare a variable with length 6 to handle:
int[] formula = new int[6];
when variable i in for loop reaches 6. The following code will throw out an error.
formula [i]= ((cw + weight) + (exam * (100 - weight)) / 100);
Have a try to declare it with length of 12.
int[] formula = new int[12];
Just paste code for method computeMarks.
public String[] computeMarks(int[] marks, int[] weighing) {
int[] formula = new int[12];
String[] results = new String[weighing.length];
StringBuilder sb = new StringBuilder();
for (int i = 0; i < weighing.length; i++) {
sb.setLength(0);
int exam = marks[i];
int cw = marks[i + weighing.length];
int weight = weighing[i];
formula[i] = ((cw + weight) + (exam * (100 - weight)) / 100);
if ((formula[i] <= 39) && (formula[i] > 35)) {
sb.append("COMPENSATION PASS");
} else if (formula[i] >= 40) {
sb.append("PASS");
} else {
sb.append("FAIL");
}
sb.append(" cw mark is ").append(cw).append(" and exam mark is ")
.append(exam);
results[i] = sb.toString();
}
return results;
}

simple maze game in java

I am creating a simple maze game in java. The code reads a data file and assigns it to a String array. The player inputs what direction he or she would like to go in and each position contains a certain number of points (0, 1, or 2) that corresponds to the obstacleNumber (0, 1, or 2). I want to keep track of the points as well as the number of times the player has moved but I cannot figure out where to call my methods to make sure that the counting is correct. I'm sorry if this is a really stupid question but I am new to java!
The array is being filled correctly but I cannot count the moves and points.
import java.util.*;
import java.io.File;
public class Program12
{
static public void main( String [ ] args ) throws Exception
{
if(args.length != 1) {
System.out.println("Error -- usage is: java Lab11 roomData.txt");
System.exit(0);
}
File newFile = new File(args[0]);
Scanner inputFile = new Scanner(newFile);
int numberOfRows = inputFile.nextInt();
int numberOfColumns = inputFile.nextInt();
Room[][] game;
game = new Room[numberOfRows][numberOfColumns];
int rowNumber = 0;
int columnNumber = 0;
int moves = 0;
int points = 0;
for(int i = 0; i < numberOfRows; i++)
{
for (int j = 0; j < numberOfColumns; j++)
{
String obstacle = inputFile.nextLine();
int obstacleNumber = inputFile.nextInt();
Room room = new Room(obstacle, obstacleNumber);
game[i][j] = room;
}
System.out.println();
countPoints(obstacleNumber, points);
}
while(true)
{
printPlayerLocation(numberOfRows, numberOfColumns, rowNumber, columnNumber);
System.out.println();
System.out.print("Enter up, down, left, or right to move: ");
Scanner userInput = new Scanner(System.in);
String in = userInput.nextLine();
if(in.equals("left") || in.equals("right") || in.equals("up") || in.equals("down"))
{
if (in.equalsIgnoreCase("up"))
{
rowNumber = rowNumber - 1;
}
if (in.equalsIgnoreCase("down"))
{
rowNumber = rowNumber + 1;
}
if (in.equalsIgnoreCase("left"))
{
columnNumber = columnNumber - 1;
}
if (in.equalsIgnoreCase("right"))
{
columnNumber = columnNumber + 1;
}
}
else
{
System.out.println("Input invalid! Please enter up, down, left, or right.");
}
try
{
System.out.println(game[columnNumber][rowNumber].toString());
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("You cannot leave the boardwalk during the hunt! Please start over.");
System.exit(0);
}
countMoves(in, moves);
//countPoints(obstacleNumber, points);
}
}
public static void printPlayerLocation(int numberOfRows, int numberOfColumns, int rowNumber, int columnNumber)
{
System.out.println();
System.out.print("***** PLAYER LOCATION *****");
String[][] game = new String[numberOfRows][numberOfColumns];
for (int i = 0; i < numberOfRows; i++)
{
for (int j = 0; j < numberOfColumns; j++)
{
game[i][j] = "*";
}
}
game[rowNumber][columnNumber] = "P";
for (int i = 0; i < numberOfRows; i++)
{
System.out.println();
for (int j = 0; j < numberOfColumns; j++)
{
System.out.printf("%-5s", game[i][j]);
}
}
System.out.println();
}
//doesn't correctly count the moves
public static void countMoves(String in, int moves)
{
if(in.equals("left") || in.equals("right") || in.equals("up") || in.equals("down"))
{
moves++;
}
System.out.println("You have used " + moves + " moves.");
}
//doesn't correctly count the points
public static void countPoints(int obstacleNumber, int points)
{
if(obstacleNumber == 0)
{
points = points;
}
if(obstacleNumber == 1)
{
points++;
}
if(obstacleNumber == 2)
{
points = points + 2;
}
System.out.println("You have obtained " + points + " points so far. Keep hunting!");
}
The data file has the size of the array (4 4) and an obstacleNumber and obstacle
0 this obstacleNumber would award 0 points
1 this obstacleNumber would award 1 point
2 this obstacleNumber would award 2 points
16 times to fill the array.
I would like the sample output to print the player location (which it does correctly), ask the user to input a direction (which it does correctly), print the text in the data file (again already done) and then print the number of moves already used and the amount of points the player has gained so far.
How do you correctly calculate the number of times the user typed "up, down, right, or left" and how many points does the user have so far? Thank you in advance for your help I sincerely appreciate your time.
The moves arent count right, because countMoves doesnt increment the Variable moves from the main function.
If you do :
System.out.println(moves);
countMoves(in, moves);
System.out.println(moves);
You will see that the value didnt changed.
So you could add a return value to countMoves and assingn moves with it :
moves = countMoves(in,moves);
Or you could increment the moves here :
if(in.equals("left") || in.equals("right") || in.equals("up") || in.equals("down"))
{
moves++;
if (in.equalsIgnoreCase("up"))
{
rowNumber = rowNumber - 1;
}
if (in.equalsIgnoreCase("down"))
{
rowNumber = rowNumber + 1;
}
if (in.equalsIgnoreCase("left"))
{ columnNumber = columnNumber - 1;
}
if (in.equalsIgnoreCase("right"))
{ columnNumber = columnNumber + 1;
}
}
The same with Points i think.
The line
points = points;
Would only make sense if you have a classvariable that would get the point value but assign a variable with its own value doenst make sense .
So maybe add a return to the countPoints and assign points with it :
points = countPoints(obstacleNumber, points);
In Java, arguments to methods are always passed by value. It can get confusing with objects though. But with primitive types like int it's very simple. To the functions countMoves and countPoints you are only giving the value of the moves and points, respectively, but not their reference. That is, the methods are working in fact with another variable. This variable is initialized to the value you give in and you can change it as you want, but the changes made to this variable are only visible to the method. Therefore in order to make the changes visibile to the outer variables you must reset their values. For instance you could do this:
public static int countMoves(String in, int moves) {
//... change the value of moves as you want
return moves; //return the new value
}
And then use the method like:
moves = countMoves(in, moves);
Where the set variable is the one you define in main. Analogously for countPoints. Another possibility would be to define moves and points in the class Program12 and make the methods count methods modify these variables directly without passing them, like:
public static void countMoves(String in) {
moves = ...
}
In this case the moves moves defined in Program12 is visible to the countMoves and so you are changing directly the variable you want; there is no need to reset it.
--
But big but. The code you have is rather spaghetti. You should think how to better structure and compartmentalize the code into closely-related logical units. In object-oriented programming you do it with classes. For example, you could define a class called GameState that keeps the variables moves and points or anything else shall you need it, and define there the count methods or other methods to modify the statistics of the game. Don't make the main method define the logic of the program. It should merely be used to read the input to initialize some sort of class Game and write to output the results of the Game.
I might be wrong here (early in the morning...) but I'm guessing you always get the same moves and points value? This is because you are not increasing the values of the actual moves and points. When you send an Int as a parameter to a method you are not sending a pointer to the Int but a copy of it which will be used by the method and then removed when leaving it. You need to either return moves and points after increasing the values or put them as static attributes. Try doing it this way instead:
...
moves = countMoves(String in, int moves);
...
public static int countMoves(String in, int moves) {
if(in.equals("left") || in.equals("right") || in.equals("up") || in.equals("down")) {
moves++;
}
System.out.println("You have used " + moves + " moves.");
return moves;
}
Or you could increase them when identifying the moving direction (which is more efficient since you don't have to redo the check if the move was valid):
if (in.equalsIgnoreCase("up")) {
rowNumber = rowNumber - 1;
moves++;
}
...
EDIT
Points Problem:
Since you didn't post how Room is implemented I just improvise, but I figure it should look something like this:
...
points = countPoints(game[rowNumber][columnNumber].getObstacleNumber(), points);
...
and change countPoints() to:
public static int countPoints(int obstacleNumber, int points) {
if(obstacleNumber == 0) points = points;
if(obstacleNumber == 1) points++;
if(obstacleNumber == 2) points += 2;
System.out.println("You have obtained " + points + " points so far. Keep hunting!");
return points;
}
or just (provided you know that the input is correct):
public static int countPoints(int obstacleNumber, int points) {
points += obstableNumber;
System.out.println("You have obtained " + points + " points so far. Keep hunting!");
return points;
}

Categories