I'm trying to optimize my program by getting rid of duplicate searches or just by generally make things optimized throughout my program and I came across a method in which I can't find any "better" solution what so ever and would love to know if anyone could point me in a direction for refineing it.
First off here is my code that I'm trying to optimize:
public Player spaceBattle(Player player1, Player player2) {
ArrayList<Ship> listOfShipsPlayer1 = this.getShipsOfPlayer(player1);
ArrayList<Ship> listOfShipsPlayer2 = this.getShipsOfPlayer(player2);
Random random = new Random();
int player1hits = 0, player2hits = 0, rolledDie;
for (Ship aShip : listOfShipsPlayer1) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player1hits += 1;
}
}
for (Ship aShip : listOfShipsPlayer2) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player2hits += 1;
}
}
for (int i = 0; i < player1hits; ++i) {
if (this.getShipsOfPlayer(player2).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer2.get(i));
} else {
break;
}
}
for (int i = 0; i < player2hits; ++i) {
if (this.getShipsOfPlayer(player1).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer1.get(i));
} else {
break;
}
}
As you can see here I run the same algorithm twice in order to check for first Player1 and then Player2 and add update their respective hits. And then for the amount of hits for each player I then remove a ship.
What I would like to know if its possible to change this bit of code to be able to not have the duplicate code for each player but that it automatically can go through player1 first and then player2 in one loop.
Looking forward to hear from you
You can just create methods.
private int hitShips(List<Ship> ships) {
int result = 0;
for (Ship ship : ships) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= ship.getShipType().getCombatValue()) {
result++;
}
}
return result;
}
which makes your code
int player1hits = hitShips(listOfShipsPlayer1);
int player2hits = hitShips(listOfShipsPlayer2);
and similar for the removal of the ships from the list.
void removeShips(List<Ship> ships, int remove) {
int removeCount = Math.max(ships.size(), remove);
Iterator<Ship> it = ships.iterator();
for (int i = 0; i < removeCount; i++) {
it.remove();
}
}
with
removeShips(getShipsOfPlayer(player2), player1hits);
removeShips(getShipsOfPlayer(player1), player2hits);
I'm not sure why you always remove ships from the head of the lists, since combat values seems to be a thing, but that's just a side note.
I wouldn't call this "optimization" so much, but the removal of duplicate code is always good.
You could optimize removeShips as:
void removeShips(List<Ship> ships, int numHits) {
int numToRemove = Math.min(ships.size(), numHits);
if (numToRemove > 0) {
ships.subList(0, numToRemove).clear();
}
}
This method call will result in only one System.arraycopy call, which will shift all remaining elements to the left.
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#subList-int-int-
Related
Note: I am a beginner in Java (2 - 3 months of experience).
Doing a project on JetBrains/Hyperskill about making a Tic Tac Toe game, I found myself repeating quite a bit of code when trying to determine the winner of the game. To represent the game as a coordinate system (Thus 1,1 being at the bottom left and 3,3 at the top right) I am using a two-dimensional array.
This is the function for determining the winner:
public String determineWinner() {
int countX = 0; // amount of X's in a row
int countO = 0; // amount of O's in a row
for (int y = 0; y <= 2; y++) { // for all horizontal rows
countX = 0;
countO = 0;
for (int x = 0; x <= 2; x++) { // loop through all x-coordinates
String value = this.field[x][y];
if (value.equals("X")) { // if the value at that coordinate equals "X", add 1 to the count
countX++;
}
if (value.equals("O")) { // same here
countO++;
}
}
if (countX == 3) { // if the count is 3 (thus 3 X's in a row), X has won
return "X wins";
}
if (countO == 3) { // same here
return "O wins";
}
}
// Same thing, but for all vertical columns
for (int x = 0; x <= 2; x++) {
countX = 0;
countO = 0;
for (int y = 0; y <= 2; y++) {
String value = this.field[x][y];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
}
// Same thing, but for diagonal
countX = 0;
countO = 0;
for (int i = 0; i <= 2; i++) {
String value = this.field[i][i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
// Same thing, but for other diagonal
countX = 0;
countO = 0;
for (int i = 0; i <= 2; i++) {
String value = this.field[i][2-i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
}
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
if (this.getNumberOfMoves() == 9) { // if the number of moves equals 9, the game is over and it is a draw
return "draw";
}
return "game not finished";
}
Currently, the code allows you to set a starting board (a starting arrangement for all the O's and X's) and then lets you do 1 move. After this, the game decides who is the winner or if it is a draw etc.
As one quickly notices, the function is way too long and it has quite a portion of repetition, yet I am unable to come up with any ways to shorten it.
Does anyone have any tips? Or any guidelines that apply to all code?
DISCLAIMER: Sorry if my answer started getting sloppy towards the end.
Also, I have a code at the bottom showing all the things I talked about in action.
I think the simplest thing I can say is to use more methods and possibly classes. Firstly, one of the ways to avoid repetition in all of your codes is to write them using object-oriented programming. This is the idea of having multiple classes that all interact with the main class to assist in writing code. I won't talk about that here, but if you are interested in making your code neat and "clean", I highly advise looking that up. Also, there is a great book on the subject called Clean Code by Robert C. Martin. I will simply be showing how you can take advantage of methods to shorten your code and clean it up. One of the things you repeat the most is this
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
Your countX and countO are different each time, so you rewrote it. I simpler and more efficient way to do this is to use a method. I would advise you to research the syntax for Java in you don't know how to make methods or classes, but you do use the syntax for the determineWinner() method so I will assume you understand it. You can make functions have parameters that are essentially inputs that can be accessed and modified throughout the function. (By the way, you cannot make methods inside methods in Java so you would need to place this next method outside somewhere else in the class.)
public String checkCounts() {
if (countX == 3) {
return "X wins";
}
if (countO == 3) {
return "O wins";
}
else return "N/A";
}
*You want to check to see if it returns "N/A" anytime you use the method with an if statement. If so, you should just ignore it since no one won.
whoWon = checkCounts();
//In the code I put at the bottom I will make whoWon a global variable, which is why I'm not defining it here.
//It will be already defined at the top of the code.
if (!whoWon.equals("N/A")) return whoWon;
*The ! symbol means not, a.k.a if whoWon does NOT equal "N/A", return whoWon.
This way, anytime you need to write out that if statement code, you can just write checkCounts and plug in the two variables that you just got from your Array. You would write checkCounts(); in this case. Now if you just say return checkCounts(); then the code will run all those if statements without you having to type them all and return the result. You actually repeat something else a lot too. These couple of lines
String value = this.field[x][y];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
are quite similar to these lines
String value = this.field[i][i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
and these lines
String value = this.field[i][2-i];
if (value.equals("X")) {
countX++;
}
if (value.equals("O")) {
countO++;
}
so you can condense them all down into one method with three different inputs. The method will return either 0, 1, or 2. The goal is to check which one it returns with the given string input and then translate that to which variable to add 1 to.
If it's 0, ignore, if it's 1, countX++, and if it's 2, countY++.
public int checkString(String value) {
int whichCount = 0;
//if whichCount is 1, it means X
//if whichCount is 2, it means O
if (value.equals("X")) {
whichCount = 1;
}
if (value.equals("O")) {
whichCount = 2;
}
return whichCount;
}
Switch statements might be a little advanced, but they're pretty simple in concept. It's a bunch of if statements all at once in a very convenient syntax. The value inside the parenthesis is your input, or what to check. The cases say, when its equal to this, do this. When you needed to increment either countX or countY inside your for loops, you would write
switch (checkString(this.field[coord1][coord2])) {
case 1 -> countX++;
case 2 -> countO++;
}
case 1 says, if addToCount() returns 1 then do the thing to the right of the arrow and case 2 says if it returns 2 to the thing to the right of that arrow. In your for loops, coord1 and coord2 could be anything from [x][y] to [i][i] to [i][2-i] so you can change that anytime you make the switch statement.
Additionally, you can turn that switch statement itself into a method.
public void adjustCounts(String stringFromArray) {
switch (checkString(stringFromArray)) {
case 1 -> countX++;
case 2 -> countO++;
}
}
You can also take a couple of lines off by shorting your if statements. If the thing inside the if statement is only one line long than you can just put in next to it.
if (bool) {
doSomething();
}
//Change that to this
if (bool) doSomething();
Another thing you repeat a lot is this
countX = 0;
countO = 0;
I just made a very simple method that does that with no parameters.
public void resetCounts() {
countX = 0;
countO = 0;
}
That's pretty much it for repetition, but I would argue your determineWinner method is still far too large. Even if you don't repeat any more code, taking large changes of it and separating it into smaller bites can make it easier to read and understand.
I added in a bunch of methods that just contained your for loops. They will be at the very bottom of this final class I came up with. It's 85 lines long so it's technically only a 4 line improvement but it's a lot cleaner. Additionally, if you were to embed this in your actual class, and not just in a single method (because you can't put it all in one method) then it would be even more efficient because you would have access to all of the classes global variables. Here is the code I came up with, but I would highly recommend doing extra research on object-oriented programming to really improve your code.
public class TicTacToe {
String[][] field = new String[3][3];
int countX, countO = 0; // amount of X's and O's in a row
String whoWon = "N/A";
public int getNumberOfMoves() {return 0;} //Whatever you method did that determined this. Obviously it didn't really just return 0.
public String determineWinner() {
String columns = checkColumnsForWinner();
String rows = checkRowsForWinner();
String diagonal1 = checkDiagonal(1, 0);
String diagonal2 = checkDiagonal(-1, 2);
if (checkForNA(columns)) return columns;
if (checkForNA(rows)) return rows;
if (checkForNA(diagonal1)) return diagonal1;
if (checkForNA(diagonal2)) return diagonal2;
if (this.getNumberOfMoves() == 9) return "draw"; // if the number of moves equals 9, the game is over and it is a draw
return "game not finished";
}
public String checkCounts(int countX, int countO) {
if (countX == 3) return "X wins";
if (countO == 3) return "O wins";
else return "N/A";
}
public int checkString(String value) {
int whichCount = 0;
//if whichCount is 1, it means X
//if whichCount is 2, it means O
if (value.equals("X")) whichCount = 1;
if (value.equals("O")) whichCount = 2;
return whichCount;
}
public void adjustCounts(String stringFromArray) {
switch (checkString(stringFromArray)) {
case 1 -> countX++;
case 2 -> countO++;
}
}
public void resetCounts() {
countX = 0;
countO = 0;
}
public String checkRowsForWinner() {
for (int y = 0; y <= 2; y++) { // for all horizontal rows
resetCounts();
for (int x = 0; x <= 2; x++) { // loop through all x-coordinates
adjustCounts(field[x][y]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
}
return "N/A";
}
public String checkColumnsForWinner() {
for (int x = 0; x <= 2; x++) {
resetCounts();
for (int y = 0; y <= 2; y++) {
adjustCounts(field[x][y]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
}
return "N/A";
}
public String checkDiagonal(int mutiply, int add) {
resetCounts();
for (int i = 0; i <= 2; i++) {
adjustCounts(field[i][i*mutiply + add]);
}
whoWon = checkCounts(countX, countO);
if (!whoWon.equals("N/A")) return whoWon;
return "N/A";
}
public boolean checkForNA(String string) {return !string.equals("N/A");}
}
In regards to Object-Oriented Programming, the best example I could see you put into practice in this example is Abstraction. This is a very general concept but I think it would help a lot in this case. In my program above, I have a TicTacToe class, and all of my code in it. The problem is, you are seeing a lot of boilerplate to get the code to run. The biggest example is the 2D Array object you have. You have to do so many things to get X's or O's out of it. It would be much better (opinion) to make a new class, maybe called Board. It would contain a private 2D Array object, and public methods to get values from that object. Additionally, (this is really just my opinion) I would recommend using an enumeration instead of Strings for you Array values. For example
public enum BoardValues {
X,
O,
EMPTY
}
You could then create a class to place these board values in essentially a 3x3 Grid.
public class Board {
private BoardValues[][] values = new BoardValues[3][3];
public BoardValues getValue(int x, int y) {
return values[x][y];
}
public BoardValues[] getRow(int rowNumber) {
BoardValues[] rowValues = new BoardValues[3];
for (int i = 0; i < values.length; i++) {
rowValues[i] = getValue(i, rowNumber);
}
return rowValues;
}
public BoardValues[] getColumn(int columnNumber) {
BoardValues[] columnValues = new BoardValues[3];
for (int i = 0; i < values.length; i++) {
columnValues[i] = getValue(columnNumber, i);
}
return columnValues;
}
public void setValues(BoardValues[][] values) {
this.values = values;
}
public void setValue(int x, int y, BoardValues value) {
values[x][y] = value;
}
}
Now instead of using that pesky old 2D Array you just create a board object and set and get it's values at will when needed. Also, I didn't add in getting diagonals but you still could quite easily, mine's just for proof of concept. This is Abstraction, probably the easiest of the OOP concepts to grasp, because it's so general. I am simply obscuring information you don't need to see when you're trying to code your game.
I'm working on 16*16 sudoku problem with DFS algorithm. Java code looks like:
public class dfs {
public boolean dfs(int[][] puzzle,int i,int j){
if(i==15&&j>=16) return true;
if(j==16){
//System.out.println(String.valueOf(i));
return dfs(puzzle,i+1,0); //{j=0; i++;
}
if(puzzle[i][j]!=-1){
return dfs(puzzle,i,j+1); //next cell in the same row
}
else{
for(int num=1;num<=16;num++){
//System.out.println("trying"+i+","+j+","+num);
if(valid(puzzle,i,j,num)){
//System.out.println(String.valueOf(num));
puzzle[i][j]=num;
if(dfs(puzzle,i,j+1)){
return true;
}
}
//else return false;
}
}
return false;
}
public boolean valid(int[][] puzzle,int x,int y,int num){
for(int i=0;i<16;i++){
if(puzzle[i][y]==num) {
//System.out.println("a");
return false;
}
}
for(int j=0;j<16;j++){
if(puzzle[x][j]==num){
//System.out.println("b");
return false;
}
}
int c=(x/4)*4;
int r=(y/4)*4;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(puzzle[c+i][r+j]==num){
//System.out.println("c");
return false;
}
}
}
return true;
}
}
And the main method is:
public static void main(String[] args) {
sudoku sudokuPuzzleGenerator = new sudoku();
long start = System.currentTimeMillis();
int numOfSudokuMatrix = 1;
List<int[][]> sudoku = new ArrayList<int[][]>();
for (int count = 1; count <= numOfSudokuMatrix; count++) {
int[][] randomMatrix = sudokuPuzzleGenerator.generatePuzzleMatrix();
int hole = 81;
while (hole > 0) {
Random randomGenerator = new Random();
int x = randomGenerator.nextInt(16);
int y = randomGenerator.nextInt(16);
if (randomMatrix[x][y] != -1) {
randomMatrix[x][y] = -1;
hole--;
}
}
for(int i=0;i<16;i++){
for(int j=0;j<16;j++){
System.out.print(randomMatrix[i][j] + " ");
}
System.out.println();
}
sudoku.add(randomMatrix);
}
System.out.println();
long start2 = System.currentTimeMillis();
for (int[][] p:sudoku) {
dfs d=new dfs();
boolean b=d.dfs(p,0,0);
for (int rowNum = 0; rowNum < 16; rowNum++) {
for (int colNum = 0; colNum < 16; colNum++) {
System.out.print(p[rowNum][colNum] + " ");
}
System.out.println();
}
}
Long end2 = System.currentTimeMillis();
Long time2 = end2 - start2;
System.out.println("It took: " + time2 + " milliseconds.");
}
When I run the code, it often terminates before all blank spaces be filled, leaving many -1 in the puzzle. I'm not sure where the problem is. I will be really thankful for any help!
It doesn't matter, but this would probably be classified as a backtracking problem, not a search. Anyway, I think this is it, but it's hard to test without that generatePuzzleMatrix method. After you check every possible number at a given cell, if you don't find an answer (hit your base case), you need to set the cell back to -1.
for(int num=1;num<=16;num++){
if(valid(puzzle,i,j,num)){
puzzle[i][j]=num;
if(dfs(puzzle,i,j+1)){
return true;
}
}
}
puzzle[i][j] = -1;
Without setting this value back to -1, you are going to leave these values set as the highest valid value, even if you didn't find an answer. Future iterations of your recursive algorithm will skip that value because they assume it is correct. Thus, your loop will end without testing all possibilities and finding an answer. Hope this does it for ya.
Comment Response
Yes, I agree there is at least 1 solution. I believe the problem is that your loop is ending before you actually test all possibilities. Your recursive calls need to set the grid back once they have tested every possibility. At an arbitrary depth in the recursive stack, say you add a valid number to your grid. The number being valid at that stage does not imply it is in the right position. You could create a ripple effect. Though it is currently valid for that particular number, you have unintentionally elimanted the possibility of any solution based on the cells you have filled so far. Once you hit that scenario, you have a problem. Your grid will retain the most recently set value, and you will never try setting it to anything else (because you have a condition that ignores values != -1).
I am trying to make a connect 4 program in java and I am stuck on the winner check. I know that I could make a very long list of if and else if's but I think loops would work better. I'm open to any other way of solving this problem but on what I've been looking at makes it seem the best. I have an idea of what to do with the rows and columns but I don't even know where to start with the diagonals. This is what I have so far:
edit:
int p1counter = 0;
int p2counter = 0;
int r = 1;//x
int c = 1;//y
for(r = 1; r <= 6; r++)
{
while(c <= 7)
{
if(grid[r][c].equals("_"))
{
c++;
p1counter = 0; p2counter = 0;
}
else if(grid[r][c].equals("1"))//player 1 counter
{
c++;
p1counter++;
}
else if(grid[r][c].equals("2"))//player 2 counter
{
c++;
p2counter++;
}
}
if(p1counter >= 4)
{
JOptionPane.showMessageDialog(null, "Player 1 is the winner!");
done = true;
}
else if(p2counter >= 4)
{
JOptionPane.showMessageDialog(null, "Player 2 is the winner!");
done = true;
}
return done;
}
In similar situations I have done the following:
Create an array of strings; as many strings as there are rows+columns+diagonals
Traverse the grid in the four possible directions (this does not include every possible diagonal since diagonals must be at least 4 long) and enter a corresponding character in the string: for example 0 (empty), 1, 2
Search the string array for 1111 and 2222.
By first organizing the data, the comparison can be done with a built in function. Much faster and cleaner.
Here is how it might be done (this is the "slow and careful way"):
class c4check {
public static void main(String[] args) {
char grid[][] = {{'e','e','e','e','a','b','a'},
{'e','a','b','a','b','b','a'},
{'e','b','a','a','b','b','a'},
{'e','a','b','b','a','b','b'},
{'e','b','a','b','b','a','a'},
{'e','a','b','a','b','b','a'}};
int ii, jj, ri, ci, di;
String checkGrid[] = new String[25];
// copy rows:
for(ri = 0; ri < 6; ri++) {
String temp = "";
for(ci = 0; ci < 7; ci++) {
temp += grid[ri][ci];
}
checkGrid[ri] = temp;
}
// copy columns:
for(ci = 0; ci < 7; ci++) {
String temp = "";
for(ri = 0; ri < 6; ri++) {
temp += grid[ri][ci];
}
checkGrid[ci + 6] = temp;
}
// copy first diagonals:
for(di = 0; di < 6; di++) {
String temp = "";
for(ri = 0; ri < 6; ri++) {
ci = di - 2;
ri = 0;
while(ci < 0) {
ri++;
ci++;
}
for(; ri < 6; ri++, ci++) {
if( ci > 6 ) continue;
temp += grid[ri][ci];
}
}
checkGrid[di+13] = temp;
}
// diagonals in the other direction:
for(di = 0; di < 6; di++) {
String temp = "";
for(ri = 0; ri < 6; ri++) {
ci = 8 - di;
ri = 0;
while(ci > 6) {
ri++;
ci--;
}
for(; ri < 6; ri++, ci--) {
if( ci < 0 ) continue;
temp += grid[ri][ci];
}
}
checkGrid[di+19] = temp;
}
for(ii = 0; ii < 25; ii++) {
System.out.println("Checking '" + checkGrid[ii] + "'");
if (checkGrid[ii].contains("aaaa")) System.out.println("Player A wins!");
if (checkGrid[ii].contains("bbbb")) System.out.println("Player B wins!");
}
}
}
Obviously, instead of copying temp to an array element, and then checking at the end, you could check for "aaaa" or "bbbb" each time, and return from the function as soon as you found a match.
Output of this particular code (which has more than one "winning" combination, so it's not a "real" situation - but it allowed me to check that all the diagonals were visited correctly):
Checking 'eeeeaba'
Checking 'eababba'
Checking 'ebaabba'
Checking 'eabbabb'
Checking 'ebabbaa'
Checking 'eababba'
Checking 'eeeeee'
Checking 'eababa'
Checking 'ebabab'
Checking 'eaabba'
Checking 'abbabb'
Checking 'bbbbab'
Player B wins!
Checking 'aaabaa'
Checking 'eaaa'
Checking 'ebbbb'
Player B wins!
Checking 'eaabbb'
Checking 'ebaaaa'
Player A wins!
Checking 'eabba'
Checking 'ebbb'
Checking 'abba'
Checking 'ababb'
Checking 'abbbaa'
Checking 'bbabbe'
Checking 'aaaae'
Player A wins!
Checking 'ebbe'
Here is way to maintain the track of winner :-
maintain 4 types of sets (horizontal,vertical,two diagonal).
whenever a point on grid is colored check with corresponding neighbour in the same type eg. if grid point (x,y) is colored then for horizontal type we check (x,y+1) and (x,y-1).
find union of two similar colored neighbour.
If size of any set is equal to four then you have found the winner. You can check while updating neighbours of point currently colored.
Suggestions: Use Union-Find datastructure to achieve good time complexity (Note as your sets are small you can do without it as well).
I am creating a concentration game.
I have an buffered image array where I load in a 25 image sprite sheet.
public static BufferedImage[] card = new BufferedImage[25];
0 index being the card back. and 1 - 24 being the values for the face of the cards to check against if the cards match.
What I am tying to do is this I will have 4 difficulties Easy, Normal, Hard, and Extreme. Each difficulty will have a certain amount of cards it will need to draw and then double the ones it chosen. for example the default level will be NORMAL which is 12 matches so it need to randomly choose 12 unique cards from the Buffered Image array and then double each value so it will only have 2 of each cards and then shuffle the results.
This is what I got so far but it always seems to have duplicates about 99% of the time.
//generate cards
Random r = new Random();
int j = 0;
int[] rowOne = new int[12];
int[] rowTwo = new int[12];
boolean[] rowOneBool = new boolean[12];
for(int i = 0; i < rowOneBool.length; i++)
rowOneBool[i] = false;
for(int i = 0; i < rowOne.length; i++){
int typeId = r.nextInt(12)+1;
while(rowOneBool[typeId]){
typeId = r.nextInt(12)+1;
if(rowOneBool[typeId] == false);
}
rowOne[i] = typeId;
j=0;
}
the 3 amounts I will be needing to generate is Easy 6, Normal 12, and Hard 18 extreme will use all of the images except index 0 which is the back of the cards.
This is more or less in the nature of random numbers. Sometimes they are duplicates. You can easily factor that in though if you want them to be more unique. Just discard the number and generate again if it's not unique.
Here's a simple method to generate unique random numbers with a specified allowance of duplicates:
public static void main(String[] args) {
int[] randoms = uniqueRandoms(new int[16], 1, 25, 3);
for (int r : randoms) System.out.println(r);
}
public static int[] uniqueRandoms(int[] randoms, int lo, int hi, int allowance) {
// should do some error checking up here
int range = hi - lo, duplicates = 0;
Random gen = new Random();
for (int i = 0, k; i < randoms.length; i++) {
randoms[i] = gen.nextInt(range) + lo;
for (k = 0; k < i; k++) {
if (randoms[i] == randoms[k]) {
if (duplicates < allowance) {
duplicates++;
} else {
i--;
}
break;
}
}
}
return randoms;
}
Edit: Tested and corrected. Now it works. : )
From what I understand from your question, the answer should look something like this:
Have 2 classes, one called Randp and the other called Main. Run Main, and edit the code to suit your needs.
package randp;
public class Main {
public static void main(String[] args) {
Randp randp = new Randp(10);
for (int i = 0; i < 10; i++) {
System.out.print(randp.nextInt());
}
}
}
package randp;
public class Randp {
private int numsLeft;
private int MAX_VALUE;
int[] chooser;
public Randp(int startCounter) {
MAX_VALUE = startCounter; //set the amount we go up to
numsLeft = startCounter;
chooser = new int[MAX_VALUE];
for (int i = 1; i <= chooser.length; i++) {
chooser[i-1] = i; //fill the array up
}
}
public int nextInt() {
if(numsLeft == 0){
return 0; //nothing left in the array
}
int a = chooser[(int)(Math.random() * MAX_VALUE)]; //picking a random index
if(a == 0) {
return this.nextInt(); //we hit an index that's been used already, pick another one!
}
chooser[a-1] = 0; //don't want to use it again
numsLeft--; //keep track of the numbers
return a;
}
}
This is how I would handle it. You would move your BufferedImage objects to a List, although I would consider creating an object for the 'cards' you're using...
int removalAmount = 3; //Remove 3 cards at random... Use a switch to change this based upon difficulty or whatever...
List<BufferedImage> list = new ArrayList<BufferedImage>();
list.addAll(Arrays.asList(card)); // Add the cards to the list, from your array.
Collections.shuffle(list);
for (int i = 0; i < removalAmount; i++) {
list.remove(list.size() - 1);
}
list.addAll(list);
Collections.shuffle(list);
for (BufferedImage specificCard : list) {
//Do something
}
Ok, I said I'd give you something better, and I will. First, let's improve Jeeter's solution.
It has a bug. Because it relies on 0 to be the "used" indicator, it won't actually produce index 0 until the end, which is not random.
It fills an array with indices, then uses 0 as effectively a boolean value, which is redundant. If a value at an index is not 0 we already know what it is, it's the same as the index we used to get to it. It just hides the true nature of algorithm and makes it unnecessarily complex.
It uses recursion when it doesn't need to. Sure, you can argue that this improves code clarity, but then you risk running into a StackOverflowException for too many recursive calls.
Thus, I present an improved version of the algorithm:
class Randp {
private int MAX_VALUE;
private int numsLeft;
private boolean[] used;
public Randp(int startCounter) {
MAX_VALUE = startCounter;
numsLeft = startCounter;
// All false by default.
used = new boolean[MAX_VALUE];
}
public int nextInt() {
if (numsLeft <= 0)
return 0;
numsLeft--;
int index;
do
{
index = (int)(Math.random() * MAX_VALUE);
} while (used[index]);
return index;
}
}
I believe this is much easier to understand, but now it becomes clear the algorithm is not great. It might take a long time to find an unused index, especially when we wanted a lot of values and there's only a few left. We need to fundamentally change the way we approach this. It'd be better to generate the values randomly from the beginning:
class Randp {
private ArrayList<Integer> chooser = new ArrayList<Integer>();
private int count = 0;
public Randp(int startCounter) {
for (int i = 0; i < startCounter; i++)
chooser.add(i);
Collections.shuffle(chooser);
}
public int nextInt() {
if (count >= chooser.size())
return 0;
return chooser.get(count++);
}
}
This is the most efficient and extremely simple since we made use of existing classes and methods.
I'm doing a combsort algorithim as a class assignment, and it loops whenever I run it. I'm not sure what I did wrong (I got it working in C++, but that was a while ago, and those skills don't translate as well as I'd like them to). I've been poring over it for an hour and a half now, and emailed some friends, but nobody has any ideas, unfortunately. I think I just need someone with some more experience to tell me what I screwed up. Thanks!
import java.util.ArrayList;
public class CombSort {
public CombSort()
{
super();
}
public ArrayList<Integer> combSort(ArrayList<Integer> sortMe)
{
int swap;
int size = sortMe.size();
int gap = size;
boolean swapped = false;
while ((gap > 1) || swapped)
{
if (gap > 1)
{
gap = (int) ((size)*((double)gap / 1.247330950103979));
}
swapped = false;
for (int i = 0; gap + i < size; ++i)
{
if (sortMe.get(i) - sortMe.get(i + gap) > 0)
{
swap = sortMe.get(i);
sortMe.set(i, sortMe.get(i + gap));
sortMe.set(i + gap, swap);
swapped = true;
}
}
}
return sortMe;
}
public static void main(String[] args)
{
ArrayList<Integer> randomArrayList = new ArrayList<Integer>(7);
randomArrayList.add(5);
randomArrayList.add(7);
randomArrayList.add(2);
randomArrayList.add(6);
randomArrayList.add(8);
randomArrayList.add(2);
randomArrayList.add(9);
CombSort combSorter = new CombSort();
System.out.println(combSorter.combSort(randomArrayList).toString());
}
}
Your gap value is getting bigger with each iteration inside the while loop, hence why it is infinitely looping.
You should only multiply the gap by the comb factor, not by the size.
(int) ((size)*((double)gap / 1.247330950103979)); should be
(int) ((double)gap / 1.247330950103979);