How do I get my set to stop adding duplicates? - java

I am making an implementation of the game Hexic. The game is centered around making clusters of Hexagons in order to remove them from the board. I have found a way to identify the coordinates of every hexagon that is part of a cluster in order to add them to a set. Some of these will be identified twice, but I only want every coordinate to be saved once which is why I chose a set.
The issue is that the coordinates get added to the set twice anyway.
The following is the relevant code:
Instantiating the set:
private Set<int[]> clusters = new HashSet<>();
The nested loop for identifying the clusters:
void findClusters() {
for (int i = 0; i < NOOFCOLUMNS; i++) {
for (int j = 1; j < NOOFROWS; j++) {
Color color = hexagons[i][j].color;
int row = j-i%2; // do not remove, else magic number
if ((hexagons[i][j-1].color == color)) {
if ((i>0)&&(hexagons[i-1][row].color==color)) { addCluster(i, j, -1); }
if ((i<9)&&(hexagons[i+1][row].color==color)) { addCluster(i, j, +1); }
}
}
}
}
The function for adding the coordinates to the set:
void addCluster(int i, int j, int x) {
clusters.add(new int[] { i, j });
clusters.add(new int[] { i, j-1 });
clusters.add(new int[] { i+x, j-i%2 });
}
Thanks in advance!

Related

Array from recursive call being overwritten

We're making a program to solve an asterisk sudoku via a recursive approach with back tracking.
The solveIt method calls the solve method which is the recursive method. grid is declared before to be a 9x9 2D array that contains the puzzle to be filled in. If there is one solution, the program must print out the completed puzzle however if there are more solutions it must only print out the number of possible solutions.
Question is: Inside of solve, print(); works just fine and prints out the complete puzzle. However outside of the method it prints out the empty initial puzzle. Why is this? We cannot figure out why a separate variable (h in this case) also gets randomly overwritten when solve completes.
int[][] h;
int solutionCounter = 0;
void solve() {
int[] next = findEmptySquare();
if (!(next[0] == -1 && next[1] == -1)) {
if (grid[next[0]][next[1]] == 0) {
for (int i = SUDOKU_MIN_NUMBER; i <= SUDOKU_MAX_NUMBER; i++) {
if (!(givesConflict(next[0], next[1], i))) {
//fills in the puzzle
grid[next[0]][next[1]] = i;
//go to next number
solve();
}
}
grid[next[0]][next[1]] = 0;
}
} else {
//print(); here it works just fine
solutionCounter++;
h = grid.clone();
}
}
void solveIt() {
solve();
if (solutionCounter > 1) {
System.out.println(solutionCounter);
} else {
grid = h.clone();
print(); //here it prints the empty puzzle
}
}
Solution
The .clone() method seems to simply reference h to grid. So h points to grid and takes on its values leading to the problem we were having above.
Therefore the following solution was implemented:
//copy the grid into h.
for (int x = 0; x < 9; x++) {
for (int y = 0; y < 9; y++) {
h[x][y] = grid[x][y];
}
}
More information on clone():
https://www.geeksforgeeks.org/clone-method-in-java-2/

algorithm visualization: implementing insertion sort without loops but with variable increments everytime a function is called

I'm building a sorting algorithm visualizer in processing (extension of java with extra libraries for visualization) and i'm very much stuck on this problem which I think others will be able to help me solve.
In processing there is a function called draw() that is being called 60 times each second. It's here that I want to execute, each time draw() is called, one step of the insertion algorithm. I already implemented it with a bubble sort. (see code below).
updateBubble() is being called in draw() and 'colors' is the name of the arraylist I use to keep the different values of colors to sort.
picture to get a better understanding:
[![visualisation algorithm preview][1]][1]
...
int j = 0
...
void updateBubble() {
bubble.sort(j);
j++;
if (i<bubble.colors.size()) {
if (j >= bubble.colors.size()-i-1) {
j = 0;
i++;
}
} else {
bubble.sorted = true;
}
}
and here is the function in the class BubbleSort (bubble is an object of this class)
void sort(int j) {
if (j<colors.size()-1) {
if (colors.get(j) > colors.get(j+1))
{
int temp = colors.get(j);
colors.set(j, colors.get(j+1));
colors.set((j+1), temp);
}
}
}
This way I was able to slow down the visualization process to the the pace of the framerate which I can control myself without using loops which would execute the sorting algorithm immediately. Now I also wanted to make a similar implementation for the insertion sort algorithm but i feel like i'm stuck because I don't seem to be able to use a similar implementation that works or there might be a better way to do this?
What I have at the moment executes it immediately as expected, without being able to see the process.
void updateInsertion() {
insertion.sort();
}
void sort() {
int n = colors.size();
for (int i = 1; i < n; ++i) {
int key = colors.get(i);
int j = i - 1;
while (j >= 0 && colors.get(j) > key) {
colors.set(j+1, colors.get(j));
j = j - 1;
}
colors.set(j+1, key);
}
}
this is what i got now: which is still wrong but is getting closer and clearifies what i'm trying to reach, making a function that only works with increments and if statements instead of whiles and fors so each different step is being executed with each call of the method.
// i resembles for loop variable
if (i<insertion.colors.size()) {
if (j<0 || insertion.colors.get(j) <= insertion.colors.get(i)) { // negative check to go out of while loop
insertion.colors.set(j+1, keap);
if(notSortedYet()){
i++;
keap = insertion.colors.get(i);
j = i - 1;
}
} else { // resembles being in the while loop
insertion.colors.set((j+1), insertion.colors.get(j));
j = j - 1;
}
}
}
EDIT: I fixed it and you can find my solution beneath :) everytime updateInsertion() is called, my code will execute exact one step in the algorithm! thanks to everyone who put effort into commenting, I dont know if this is best practise, so keep me updated on that if you want!
void updateInsertion() {
// i resembles for loop variable
if (i<insertion.colors.size()) {
if (j>=0 && insertion.colors.get(j) > firstUnsorted) {
int temp = insertion.colors.get(j+1);
insertion.colors.set((j+1), insertion.colors.get(j));
insertion.colors.set(j,temp);
j = j - 1;
} else {
insertion.colors.set(j+1, firstUnsorted);
if (i<insertion.colors.size()-1) {
i++;
}
firstUnsorted = insertion.colors.get(i);
j = i - 1;
}
}
}
I love this project.
Processing also have a millis() method which returns how many milliseconds were spent since you've started your sketch. I sometimes use it to time my animations, which could come in handy right here. Here's an implementation of a timer class:
class Delay {
int limit;
Delay (int l) {
limit = millis() + l;
}
boolean expired () {
return (millis() > limit);
}
}
I suggest that you use this class instead of tweaking the FPS. By using the Delay to slow down your implementation of the sort, you're letting the computer work at it's own rhythm and only draw a new frame when you need it. Like this (excuse the parts where I say "do stuff"):
Delay holdTheFrame = new Delay(1);
void draw() {
if(holdTheFrame.expired()) {
holdTheFrame = new Delay(500); // half a second before the next frame
// Advance one step forward in your sorting
// Draw the visualization of the data
}
}
You can fine tune at what pace your data is sorted and only paint it when it changes. It's win-win!
Have fun!
EDIT
To help you with the implementation, here's an example one. You can copy and paste this code in an empty Processing sketch and it'll run as-is. To make things easier on my side I print to console instead of using the graphical display, but you should be able to get what I'm doing.
The secret here is that my sorting algorithm have been subtly modified so they instead always run only ONE sorting step when I call them. See for yourself:
int _numberOfItems = 10;
int _sortingStep = 0;
IntList _bubbleList = new IntList();
boolean _bubbleListSorted = false;
IntList _selectionList = new IntList();
IntList _insertionList = new IntList();
Delay _delay = new Delay(1);
void setup() {
for (int i=0; i<_numberOfItems; i++) {
_bubbleList.append((int)random(10, 99));
}
for (int i=0; i<_numberOfItems; i++) {
_selectionList.append((int)random(10, 99));
}
for (int i=0; i<_numberOfItems; i++) {
_insertionList.append((int)random(10, 99));
}
}
void draw() {
if (_delay.expired()) {
_delay = new Delay(500);
// sort one step with every algo you want to display
if (!_bubbleListSorted) {
singleStepBubbleSort(_bubbleList);
}
if (_sortingStep < _numberOfItems) {
singleStepSelectionSort(_selectionList, _sortingStep);
singleStepInsertionSort(_insertionList, _sortingStep);
}
_sortingStep++;
// update the display (I'm printing to console instead for simplicity)
for (int i : _bubbleList) {
print(i + " ");
}
print(" | ");
for (int i : _selectionList) {
print(i + " ");
}
print(" | ");
for (int i : _insertionList) {
print(i + " ");
}
print("\n");
}
}
// An "single-step" implementation of Insertion Sort
void singleStepInsertionSort(IntList list, int step) {
int k = list.get(step);
int j = step - 1;
while (j >= 0 && list.get(j) > k) {
list.set(j+1, list.get(j));
j = j - 1;
}
list.set(j+1, k);
}
// An "single-step" implementation of Bubble Sort
void singleStepBubbleSort(IntList list) {
int temp;
boolean swapped = false;
for (int i=0; i<list.size()-1; i++)
{
if (list.get(i) > list.get(i + 1))
{
// swap arr[j] and arr[j+1]
temp = list.get(i);
list.set(i, list.get(i+1));
list.set(i+1, temp);
swapped = true;
}
}
if (!swapped) {
_bubbleListSorted = true;
}
}
// An "single-step" implementation of Selection Sort
void singleStepSelectionSort(IntList list, int step)
{
int min_idx = step;
for (int j = step+1; j < list.size(); j++) {
if (list.get(j) < list.get(min_idx)) {
min_idx = j;
}
}
int temp = list.get(min_idx);
list.set(min_idx, list.get(step));
list.set(step, temp);
}
class Delay {
int limit;
Delay (int l) {
limit = millis() + l;
}
boolean expired () {
return (millis() > limit);
}
}
Let me know if you have questions.
MORE EDITS:
Every swap of an insertion sort means many, many swaps. It's a real pain because this algorithm is kinda complicated to stop in it's tracks.
Luckily, I don't care. Thinking outside the box, I opted instead to create a class dedicated to sort an array while recording how to sort it, then be able to play it back "as if it was happening in real time". take a look:
int numberOfItems = 10;
int sortingStep = 0;
Delay delay = new Delay(1);
ManagedSelectionSort managedSelectionSort; // I created a class just to manage this madness
void setup() {
IntList list = new IntList();
for (int i=0; i<numberOfItems; i++) {
list.append((int)random(10, 99)); // some random numbers to sort later
}
managedSelectionSort = new ManagedSelectionSort(list); // take a look at the instantiation of this class
print("Step " + String.format("%02d", sortingStep) + ": ");
printArray(managedSelectionSort.list);
print("\n");
}
void draw() {
if (delay.expired()) {
delay = new Delay(100); // i put a very short delay, you'll probably want to tweak this
managedSelectionSort.sortOneStep(); // this is not what it seems
sortingStep++;
print("Step " + String.format("%02d", sortingStep) + ": ");
printArray(managedSelectionSort.list);
print("\n");
}
}
// this class is where the magic happens
// we'll sort the array all at once while recording every move
// then we'll play back those moves on a copy of the array
class ManagedSelectionSort {
IntList list, hiddenList; // list is the "official" list, while hiddenList is where the heavy lifting happens
ArrayList<SwapIndex> swapList; // this is where I record how to sort the array
ManagedSelectionSort(IntList baseList) { // this way I can instantiate several similar objects with the same list
list = new IntList();
hiddenList = new IntList();
swapList = new ArrayList<SwapIndex>();
for (int i : baseList) {
// both lists have the same initial numbers
list.append(i);
hiddenList.append(i);
}
// as soon as this object is instantiated, it knows how it'll sort the array
// because it already did...
hiddenSort();
}
// this method plays the moves which were recorded earlier according to the current sortingStep
// the swapList array was filled with every swap needed to sort the array, one by one
// now it's just a matter of playing them back on a copy of the initial array
void sortOneStep() {
if (sortingStep < swapList.size()) {
swap(list, swapList.get(sortingStep).index1, swapList.get(sortingStep).index2);
}
}
// this is the real implementation of the insertion sort
void hiddenSort()
{
for (int i=1; i<hiddenList.size(); i++) {
int j = i;
while (j>0 && hiddenList.get(j) < hiddenList.get(j-1)) {
swap(hiddenList, j, j-1, true); // swap is a class specific helper method, it swaps the numbers and also records the move
j--;
}
}
}
// this is an overload, i could have done without but it's confortable
void swap(IntList list, int index1, int index2) {
swap(list, index1, index2, false);
}
void swap(IntList list, int index1, int index2, boolean recordMove) {
// the swap first
int temp = list.get(index1);
list.set(index1, list.get(index2));
list.set(index2, temp);
// if the method is set on 'record', it adds this move to the swapList array
if (recordMove) {
swapList.add(new SwapIndex(index1, index2));
}
}
}
// this class could have been a struct, but I like to start in OOP right from the bat in case things gets complicated
class SwapIndex {
int index1;
int index2;
SwapIndex(int index1, int index2) {
this.index1 = index1;
this.index2 = index2;
}
}
// this method is just an helper method to print to console
void printArray(IntList list) {
for (int i : list) {
print(i + " ");
}
}
class Delay {
int limit;
Delay (int l) {
limit = millis() + l;
}
boolean expired () {
return millis() > limit;
}
}
This should solve your initial problem, if I understood it right this time!
One way to achieve this is via a some sort of stored state. Below is at a high level what I'm talking about.
// Starts the procedure. Must be called before draw().
void init() {
state = "forLoop";
i = 1;
n = colors.size();
}
// Single iteration of a loop.
void draw(){
switch(state) {
case "forLoop":
doForBody();
break;
case "whileLoop":
doWhileLoopBody();
break;
...
}
}
// Executes everything in the while loop and the one or two things
// just after it.
void doWhileLoopBody() {
if (isThisIterationOfWhileDone()) {
// Get out of the while loop and prepare for the next iteration of for.
// A better way to what I'm doing on the next couple lines here would
// be to introduce an additional state (ex: "postWhile") that would
// execute just after this method and would handle the colors.set(),
// incrementing i, etc.
state = "forLoop";
colors.set(j+1, key);
i++;
return;
}
// update colors, value of j, etc...
}
// Executes everything before the while loop.
void doForLoopBody() {
if (isThisIterationOfForDone()) {
state = "END";
return;
}
// update colors, get values of key and j initialized, etc
// switch to processing the body of the while loop
state = "whileLoop";
}

How to prevent string overlap on a 2D array?

I'm making battleships and I've currently come across an issue where my ships overlap. I've tried to incorporate an if statement that will judge whether it can be placed. Here's an example of me placing two ships of length 3 down.
public static void PlaceCruiser(String[][] board) {
ThreadLocalRandom random = ThreadLocalRandom.current();
int timesplaced = 0;
int size = 3;
while (timesplaced < 2) {
int randomcruisercheck =(int)(Math.random()*2);
if (randomcruisercheck == 0) {
int column = random.nextInt(0,9);
int row = random.nextInt(0,7);
if (row + 2 < 11 && board[row][column] == "." && board[row + 1][column] == "." && board[row + 2][column] == ".") {
for(int i = 0; i<size; i++)
{
board[row+i][column] = "#";
}
System.out.println(board[row][column]);
}
timesplaced++;
}
else if (randomcruisercheck == 1) {
int column = random.nextInt(0,9);
int row = random.nextInt(0,7);
if (column + 2 < 11 && board[row][column] == "." && board[row][column + 1] == "." && board[row][column + 2] == ".") {
for (int i = 0; i<size; i++)
{
board[row][column + i] = "#";
}
System.out.println(board[row][column]);
}
timesplaced++;
}
}
}
Basically, I use "#" to represent a ship in a 10x10 2D array of ".". I feel like the if statement about if the row or column plus 1 then plus 2 is a dot i.e a free space, a ship will be generated but this does not seem to be the case. Can anyone help me out?
Your code works well, you only need to take care of the indexes and initialize the board:
public class Main {
public static String[][] board;
public static void main(String[] args) {
PlaceCruiser pc = new PlaceCruiser();
board = new String[10][10];
// Initialize the board
for (int i=0;i<10;i++) {
for (int j=0;j<10;j++) {
board[i][j]=".";
}
}
pc.placeCruiser(board);
// Show thew board
for (int i=0;i<10;i++) {
for (int j=0;j<10;j++) {
System.out.print(board[i][j]);
}
System.out.println();
}
}
}
Result:
..........
..###.....
..........
..........
....#.....
....#.....
....#.....
..........
..........
..........
Also check that your initial position is not already "#".
Stylistical remarks:
if you use ThreadLocalRandom for generating position, you should also use it for other randomness (in other words: (int)(Math.random()*2) could rather be random.nextBoolean(), because actually a boolean could decide if ship should be horizontal or vertical)
nextInt(0,x) is just a longer variant of nextInt(x).
Actual bugs:
due to a presumably copy-paste issue, column (0-"9") and row (0-"7") are generated in the same way in both cases, making it possible to index out of the array when placing a vertical ship
which you seem to have noticed, but fixed it with that row + 2 < 11 check which has two problems in itself:
when row+2 ends up being 10 (which is <11), that is an invalid index (valid indices are 0...9)
as row stays between 0 and "7", there will not be horizontal ships in the last few rows
nextInt(a,b) generates numbers a...b-1, so it will not generate b itself
as the other answer points out string comparison with == generally and usually does not work, use equals()
Generally I would suggest having a single check+placement function, which can deal with an entire rectangle (given position+size). Also, I switched to array of characters, that simplifies both comparisons and printing.
boolean tryPlace(int x,int y,int width,int height) {
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
if(board[y+i][x+j]!='.') {
return false; // ship can not be placed
}
}
}
// if we reach here, ship can be placed
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
board[y+i][x+j]='#';
}
}
return true; // ship placed successfully
}
This routine could be called to place a pair of 3-long ships this way:
board=new char[10][10];
for(int i=0;i<10;i++)
for(int j=0;j<10;j++)
board[i][j]='.';
int size=3;
int amount=2;
while(amount>0) {
if(random.nextBoolean()) {
// horizontal
if(tryPlace(random.nextInt(10-size+1),random.nextInt(10),size,1)){
amount--; // one placed
}
} else {
// vertical
if(tryPlace(random.nextInt(10),random.nextInt(10-size+1),1,size)){
amount--; // one placed
}
}
}
// and a 4x2 mothership
while(!(random.nextBoolean()
?tryPlace(random.nextInt(7),random.nextInt(9),4,2)
:tryPlace(random.nextInt(9),random.nextInt(7),2,4)
));
for(int i=0;i<10;i++)
System.out.println(board[i]); // char[] has special overload for print/ln()
Test: https://ideone.com/DjYqjB
However, when I was a kid we had a rule that ships could not match, there had to be empty space (or a border of the board) around them. If you need that, tryPlace() could check a larger block, and put the ship into the middle of it. Also, a usual trick of implementing board games is that you can keep a larger array in the memory than what you will actually display. So instead of fighting with "check if field is empty or it is outside the board", it is simpler to have a 12x12 board, and place ships into the middle 10x10 portion of it:
boolean tryPlaceWithBorder(int x,int y,int width,int height) {
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
if(board[y+i][x+j]!='.')
return false; // ship can not be placed
// if we reach here, ship can be placed
for(int i=1;i<height-1;i++)
for(int j=1;j<width-1;j++)
board[y+i][x+j]='#';
return true; // ship placed successfully
}
and modified usage:
board=new char[12][12];
for(int i=0;i<12;i++)
for(int j=0;j<12;j++)
board[i][j]='.';
int size=3;
int amount=2;
while(amount>0) {
if(random.nextBoolean()) {
// horizontal
if(tryPlaceWithBorder(random.nextInt(12-size-1),random.nextInt(10),size+2,3))
amount--; // one placed
} else {
// vertical
if(tryPlaceWithBorder(random.nextInt(10),random.nextInt(12-size-1),3,size+2)){
amount--; // one placed
}
}
}
// and a 4x2 mothership
while(!(random.nextBoolean()
?tryPlaceWithBorder(random.nextInt(7),random.nextInt(9),6,4)
:tryPlaceWithBorder(random.nextInt(9),random.nextInt(7),4,6)
));
for(int i=1;i<11;i++)
System.out.println(String.valueOf(board[i],1,10));
Test: https://ideone.com/LXAD7T

2048 change check method java

I am trying to write a 2048 game in java. I am trying to make it so it checks if the board has been changed, and if it was changed it will add to the move counter and add a number to the board. Otherwise it should not do anything. I am running into a bug where the method that checks if it was changed returns true every time and I can't seem to figure out why.
This is my isChecked method which should return true if the board has been changed and false otherwise.
public boolean isChanged(int [][]copy,int [][]orig){
if(copy.length!=orig.length){
System.out.print("INVALID MOVE");
return false;
}
for(int i=0;i<copy.length;i++){
for(int j=0;j<copy[i].length;j++){
if(copy[i][j]!=orig[i][j]) {
System.out.print("INVLAID MOVE");
return false;
}
}
}
System.out.println("VALID MOVE");
moves++;
return true;
}
Below are the method that handle left movement, combination, etc. the ones for up down and right are basically the same just with minor changes to change the direction so I decied not to include them in this post as I did not feel they were necessary
public void shiftLeft() {
for (int x = 0; x < board.length; x++) {
for (int y = board[x].length-1; y>0; y--) {
if (board[x][y -1] == 0 && board[x][y] != 0) {
board[x][y - 1] = board[x][y];
board[x][y] = 0;
if(y!=board[x].length-1)
y+=1;
}
}
}
}
public void combineLeft() {
for (int x = 0; x < board.length; x++) {
for (int y =board[x].length-2; y >=0; y--) {
if(board[x][y]==board[x][y+1]){
board[x][y]*=2;
board[x][y+1]=0;
}
}
}
}
public void left(){
int [][] copy=board.clone();
shiftLeft();
shiftLeft();
combineLeft();
shiftLeft();
if(isChanged(copy,board)==true)
addNum();
}
addNum() is simply a function that adds a number to a random empty position on the board. board is the class variable(these are all in the same class) which is a 2d int array which represents the game board.
Check the ischanged function. You are returning false if the corresponding values are not equal. Actually that means you are returning false if the board is not changed.
Or just do this:
if(copy[i][j]==orij[i][j]) //here I just replaced “!=“ with “==“
return false;
Also like #Talik said use deep copy
try using:
Arrays.copyOf(..)
I think clone just copies the reference on the arrays of the board into a new array. So every time you change board, you change the clone
other options are as seen here:
How to clone a multidimensional array in java?
a deep copy method
public static int[][] deepCopyIntMatrix(int[][] input) {
if (input == null)
return null;
int[][] result = new int[input.length][];
for (int r = 0; r < input.length; r++) {
result[r] = input[r].clone();
}
return result;
}
and cloning each row in the array manually

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];
}
...

Categories