Hi so I've recently started programming in java and I've set myself a task of making an AI for a tic tac toe game I've made
However the minmax algorithm is throwing a Stack Overflow error and I cant see in the error or the program where the problem is.
Here's the program:
public State minmax(boolean max, State currentState)
{
if (currentState.getNull() == 0) {
return currentState;
}
else {
State[] successorStates = currentState.getSuccessorStates(aiPlayer);
ArrayList<Integer> scoresTemp = new ArrayList<>();
for (State state : successorStates) {
scoresTemp.add(evaluate(aiPlayer, minmax(!max, state)));
}
Integer[] scores = (Integer[]) scoresTemp.toArray();
if (max) {
State maxState = successorStates[0];
int maxScore = evaluate(aiPlayer, maxState);
for (int score : scores) {
if (scores[0] > maxScore) {
maxScore = score;
maxState = successorStates[score];
}
}
return maxState;
}
else
{
State minState = successorStates[0];
int minScore = evaluate(aiPlayer, minState);
for (int score : scores) {
if (scores[0] > minScore) {
minScore = score;
}
}
return minState;
}
}
}
It returns the state which is the best move to make.
getNull() returns the amount of spaces left that can be played on.
getSuccesorStates(Player) returns all of the succeeding states of that state by making a new state of which contains the old moves and a new one of the Player.
evaluate() returns the value -1, 0 or 1 depending on a win, draw or loss in that state. None returns 0
edit:
public int getNull()
{
int amount = 0;
for (int x =0; x<9; x++)
{
if (getAllCells()[x]==null)
{
amount++;
}
}
return amount;
}
public State[] getSuccessorStates(Player player)
{
State[] states = new State[getNull()];
Player[][] stateCells = cells.clone();
int[][] nullPositions = getNulls();
for (int x=0; x<getNull(); x++)
{
stateCells[nullPositions[x][0]][nullPositions[x][1]] = player;
states[x] = new State(player, stateCells);
stateCells = cells.clone();
}
return states;
}
Caused by: java.lang.StackOverflowError
at sample.AI.minmax(AI.java:23)
at sample.AI.minmax(AI.java:32)
at sample.AI.minmax(AI.java:32)
.
.
.
23: if (currentState.getNull() == 0)
32: scoresTemp.add(evaluate(aiPlayer, minmax(!max, state)));
public Player[] getAllCells()
{
Player[] cellList = new Player[9];
for (int x = 0; x<3; x++)
{
for (int y = 0; y<3; y++)
{
cellList[y*3+x] = cells[x][y];
}
}
return cellList;
}
minmax is called in:
public Ply getPly(State state)
{
State bestState = minmax(true, state);
State[] successorStates = state.getSuccessorStates(aiPlayer);
ArrayList<State> states = new ArrayList<State>();
for (int x=0; x<successorStates.length; x++)
{
states.add(successorStates[x]);
}
int[][] nulls = state.getNulls();
Ply bestPly = new Ply(aiPlayer, nulls[states.indexOf(bestState)][0], nulls[states.indexOf(bestState)][1]);
return bestPly;
}
Thankyou if anyone could help:)
Your problem is here:
scoresTemp.add(evaluate(aiPlayer, minmax(!max, state)));
When you call the minmax method you create a bunch of data that uses up the memory ( java allows a certain amount of the computers memory to be used ).
You then inside minmax call minmax again making it create even more data and this is happening infinitely until there is no more memory left and Java throws the StackOverflow exception.
Related
I have two classes: class Creature which contains ArrayList boids, and class Food.
Boids have a few parameters:
Creature(float posX, float posY, int t, int bth, int ah) {
location = new PVector(posX, posY);
vel = new PVector(random(-5,5), random(-5, 5));
acc = new PVector();
type = t;
if (t == 1) { btype = bth; }
else { health = bth; }
if (t == 1) { age = ah; }
else { hunger = ah; }
wdelta = 0.0;
action = 0;
if (btype == 1) { mass = 5.0; }
else { mass = 7.0; }
}
Food class has this method:
void foodtime(ArrayList boids) {
for (int i = 0; i < boids.size(); i++) {
Creature boid = (Creature) boids.get(i);
float distance = PVector.dist(location, boid.location);
if (distance < 0.5) {
bnumadj = i;
count++;
if (count == quantity) {
planet.food.remove(this);
count = 0;
bnumadj = -1;
}
}
}
}
What I'm trying to achieve is that if a boid "eats" the food, their boid type (btype) changes from 2 to 1.
I'm trying to use bnumadj variable to feed it back to the boid in this method:
void boid(ArrayList boids) {
for (int i = 0; i < boids.size(); i++) {
if (i == bnumadj) {
this.btype = 1;
bnumadj = -1;
}
}
}
Where am I going wrong?
This seems like a very convoluted way to do this, so I'm not surprised you're having issues. You're comparing values to indexes, which doesn't make a ton of sense to me.
Instead, try using a simple nested loop to do what you want. You can use an Iterator to make it easier to remove items while iterating.
ArrayList<Creature> boids = new ArrayList<Creature>();
ArrayList<Food> food = new ArrayList<Food>();
//populate ArrayLists
void draw(){
for(Creature boid : boids){
Iterator<Food> foodIter = food.iterator();
while(foodIter.hasNext()){
Food f = foodIter.next();
float distance = PVector.dist(boid.location, food.location);
if (distance < 0.5) {
boid.btype = 1;
foodIter.remove(); //removes the food
}
}
}
//draw the scene
}
I suppose you could move the second iteration using the Iterator inside the Creature type, but the basic idea is this: keep it simple by using an Iterator to remove the Food instead of trying to match indexes.
I have a project for school to take my current pacman program and have pacman traverse the 2-d char array to find a pattern to complete the maze.
I am trying to use a stack and add specific index of my maze to the stack so when pacman reaches the index it pops off and he chooses a direction randomly to another index and so forth until all dots are eaten. My issue is taking my 2d array and finding the index values of the intersections and then adding them to a stack.
I am new to java and not really sure how to approach this I posted my maze I have and some code that will use the stack to find the sequence. Thanks in advanced.
private void create() {
String[] tier = new String[tall];
tier[0] = "|======================================|";
tier[1] = "|......................................|";
tier[2] = "|.====.==========================.====.|";
tier[3] = "|.||||............................||||.|";
tier[4] = "|.====.==========================.====.|";
tier[5] = "|......................................|";
tier[6] = "|.====================================.|";
tier[7] = "|......................................|";
tier[8] = "|.====.======.====----====.======.====.|";
tier[9] = "|.||||........|| ||........||||.|";
tier[10] = "|.||||.======.|| ||.======.||||.|";
tier[11] = "|.||||........|| ||....... ||||.|";
tier[12] = "|.====.======.============.======.====.|";
tier[13] = "|......................................|";
tier[14] = "|.===========..............===========.|";
tier[15] = "|.............===== =====.............|";
tier[16] = "|.=======.===.||$ $||.===.=======.|";
tier[17] = "|.|||||||.===.===== =====.===.|||||||.|";
tier[18] = "|.|||||||......................|||||||.|";
tier[19] = "|.|||||||.====================.|||||||.|";
tier[20] = "|.=======......................=======.|";
tier[21] = "|.==......====================......==.|";
tier[22] = "|.==.====.======........======.====.==.|";
tier[23] = "|....====........======........====....|";
tier[24] = "|======================================|";
for (int i = 0; i < tall; i++) {
array[i] = tier[i].toCharArray();
}
}
public boolean isPattern(Spot b, Spot e) {
Stack<Spot> ss = new Stack<Spot>();
Spot topPost, nextPost;
allOld();
ss.push(b);
markOld(b);
topPost = ss.peek();
while (!ss.empty() && (topPost.compareTo(e) != 0)) {
nextPost = getNextSpot(topPost);
if (nextPost == null) {
ss.pop();
} else {
ss.push(nextPost);
markOld(nextPost);
}
topPost = ss.peek();
}
if (ss.empty()) {
return false;
} else {
return true;
}
}
public void index(int x, int y, int dx, int dy){
x=array.length;
y=array[x].length;
for( int i=0;i <x;i++){
for(int j =0; j<y;j++){
if(array[i].get(i))//this should determine if its aninersection and then add to the stack
}
}
}
}
the last method is where i am getting stuck. I dont know how to get the specific index values. I guess i can enter them manually but I am sure there is an easier way. Please comment if need more context on the problem and Thanks again.
I'm trying to build simple multithreading application. But I'm confused about Java monitors. I have many threads that want to format with their data one array. So for example I have Supermarket Threads (data of the thread is in txt file) So first thread have these product (Milk, Cheese, Chocolate) and country code for each product 1,2, 3
SupermarketA
Milk 1
Cheese 2
Chocolate 3
SupermarketB
Yogurt 1
Orangle 2
Bannana 3
Tea 7
Kiwi 9
and I want to format array that has to fields (country_code and count)
So my array should look like that
Country_code count
1 2
2 2
3 2
7 1
9 1
Code
public class SortedArray{
private int num = 0; // num is country code
private int count = 0;
}
So here's my monitor class
public class SingleArray {
private SortedArray[] array;
private int arrayIndex;
private static final int MAX_SIZE = 5;
public SingleArray() {
array = new SortedArray[MAX_SIZE];
arrayIndex = 0;
initArray();
}
private void initArray() {
for (int i = 0; i < MAX_SIZE; i++) {
array[i] = new SortedArray();
}
}
public synchronized void inc(){
awaitUnderMax();
notifyAll();
}
private void awaitUnderMin(){
while (arrayIndex == 0) try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void dec(){
awaitUnderMin();
notifyAll();
}
public void add(ArrayList<Integer> count){
for (int i = 0; i < count.size(); i++) {
singleArray.inc();
int num = count.get(i);
if (singleArray.arrayIndex == 0) { // if array is empty add value to it
singleArray.array[0].num = num;
singleArray.array[0].count++;
singleArray.arrayIndex++;
} else {
if (!isThere(num)) { // if num is a new value to array
singleArray.inc();
int index1 = singleArray.arrayIndex;
if (num > singleArray.array[index1 - 1].num) {
singleArray.inc();
singleArray.array[index1].num = num;
singleArray.inc();
singleArray.array[index1].count++;
singleArray.inc();
singleArray.arrayIndex++;
System.out.println(Thread.currentThread().getName() + " first " + singleArray.array[index1].num);
} else if (num < singleArray.array[index1 - 1].num) { // jei num mazesne uz paskutinia masyvo reiksme
int index = index1 - 1 < 0 ? index1 : index1 - 1;
while (index > 0 && num < singleArray.array[index].num) {
index--;
}
if (index != singleArray.arrayIndex) {
System.out.println(Thread.currentThread().getName() + " sec " + singleArray.array[index].num);
singleArray.array = addPos(singleArray.array, index + 1, num);
}
}
}
}
}
}
public boolean isThere(int number){
for(int i=0; i<singleArray.arrayIndex; i++){
singleArray.inc();
if(number == singleArray.array[i].num){
singleArray.array[i].count++;
return true;
}
}
return false;
}
private void awaitUnderMax(){
while (arrayIndex >= MAX_SIZE) try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void removeValue(int number, int howManyItems){
for(int i=0; i<arrayIndex; i++){
dec();
if(number == array[i].num){
int numberToDelete = array[i].count - howManyItems >= 0 ? howManyItems : array[i].count;
if(array[i].count >= numberToDelete){
array[i].count -= numberToDelete;
}
if(array[i].count == 0){
deleteItem(i);
}
}
if(array[i].count == 0){
deleteItem(i);
}
}
}
Each thread call add(ArrayList<Integer> count) method
So basically what add method does:
Find place where to insert new value (dependng if new value is greater or lower than a previous)
call isThere(int num) method that check if new value is already in array (if so increment count singleArray.array[i].count++) otherwise add new value to array
If array is full arrayIndex == MAX_SIZE wait current thread for other threads to decrement arrayIndex (this is oly one part of code I also have other threads that based on county code decrement array)
So the biggest problem is that multiplethreads need to update single array at the same time (I know that adding synchronized keyword to add method should solve this problem but it only let one thread to run this method at once!) So sometimes all works fine, but sometimes I get really starnge results (for example that country code is 0 (That is imposible!!!) and sometimes new values is placed in wrong array posiitons). Also I think that semaphores should solve this problem, but is it possible to do that with monitors? Thank's for the answers.
EDIT v2
to #Elyasin
public Thread[] setUpShopsBuilderThreads(){
int size = data.getSize();
ArrayList<ArrayList<String>> a = new ArrayList<>();
ArrayList<ArrayList<Integer>> b = new ArrayList<>();
ArrayList<ArrayList<Double>> c = new ArrayList<>();
Thread[] threads = new Thread[size];
for (int i = 0; i < size; i++) {
int tmp = data.getIndex(i);
int range = i + 1 < size ? data.getIndex(i + 1) : data.getWaresSize();
ArrayList<String> name = new ArrayList<>();
ArrayList<Integer> count = new ArrayList<>();
ArrayList<Double> price = new ArrayList<>();
for (int j = tmp; j < range; j++) {
name.add(data.getName(j));
count.add(data.getCount(j));
price.add(data.getPrice(j));
}
a.add(name);
b.add(count);
c.add(price);
}
procesas_1 p1 = new procesas_1(a.get(0), b.get(0), c.get(0));
procesas_2 p2 = new procesas_2(a.get(1), b.get(1), c.get(1));
procesas_3 p3 = new procesas_3(a.get(2), b.get(2), c.get(2));
procesas_4 p4 = new procesas_4(a.get(3), b.get(3), c.get(3));
procesas_5 p5 = new procesas_5(a.get(4), b.get(4), c.get(4));
Thread worker1 = new Thread(p1);
Thread worker2 = new Thread(p2);
Thread worker3 = new Thread(p3);
Thread worker4 = new Thread(p4);
Thread worker5 = new Thread(p5);
threads[0] = worker1;
threads[1] = worker2;
threads[2] = worker3;
threads[3] = worker4;
threads[4] = worker5;
return threads;
}
public static void main(String[] args) {
Starter start = new Starter();
start.read();
start.printShopsData();
start.printUserData();
Thread[] builderThreads = start.setUpShopsBuilderThreads();
for(int i=0; i<builderThreads.length; i++){
builderThreads[i].start();
}
}
what about using the concurrent safe datasets java already provides?
if you want it sorted, this one looks it might work for you:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentSkipListSet.html
just add it as in a normal Collection
I'm trying to display 550 data points with periodic peaks (the flat line is 61). The problem is, that androidplot isn't drawing all the points correctly! From my log:
ECG I values 61,61,62,63,62,61,61,61,61,67,71,68,61,53,61,61,61,61,61,61,61,61,62,63,64,64,64,63,62,61,61,61
I've got the rangeboundaries set to plot.setRangeBoundaries(0,100, BoundaryMode.AUTO);, but as you can see, the peaks never drop to the 53 data point. I can see this lower point sometimes, but it gets smoothed out a fraction of a second later (as you can see in the screenshot).
My line and point formatter is:
LineAndPointFormatter lapf = new LineAndPointFormatter(p.color, null, null, null);
lapf.getLinePaint().setStrokeJoin(Paint.Join.MITER);
lapf.getLinePaint().setStrokeWidth(1);
I've tried with the both Paint.Join.ROUND and Paint.Join.BEVEL and got the same effect. I've also used the debugger to check that 53 is being inserted into the series.
EDIT
After some debugging, it looks like my pulse loop thread is wrong:
while (keepRunning) {
for (PulseXYSeries j : series) {
for (int k = 0; k < j.plotStep; k++) {
int at = (j.position + k) % j.getSize();
if (j.pulsing) {
if (j.pulsePosition == j.pulseValues.size() - 1) {
j.pulsing = false;
j.pulsePosition = 0;
} else {
try {
int pulseVal = j.pulseValues.get(j.pulsePosition);
j.setY(pulseVal,at);
j.pulsePosition += 1;
} catch(IndexOutOfBoundsException e) {
j.pulsePosition = 0;
}
}
} else {
j.setY(j.pulseValues.get(0), at);
long currTime = SystemClock.elapsedRealtime();
if (currTime - j.getLastPulse() >= j.getPulseDelay()) {
j.pulsing = true;
j.setLastPulse(currTime);
}
}
j.remove(((at + j.eraserSize) % j.getSize()));
}
j.position = (j.position + 1) % j.getSize(); // fixed it by changing +1 to + j.plotStep
}
Thread.sleep(delay);
}
My custom series looks like:
private class PulseXYSeries implements XYSeries {
private List<Integer> pulseValues = new ArrayList<Integer>();
private int pulsePerMinute;
public int pulsePosition;
public int position;
private ArrayList<Integer> values;
private String title;
private long lastPulse;
public boolean pulsing = false;
public int eraserSize = 20;
public int plotStep = 3;
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
Can't figure this out, I've created a simple class of coordinates to hold x and y ints. In another class I have a global array of Coordinates declared called "ords". In my loop I'm adding Coordinates. When trying to use method getX() and getY() from the Coordinates class in my getaction method, I get a null pointer exception. I'm sure the objects are not null, but I still can't figure out whats going wrong. Any help appreciated.
import java.util.*;
import org.w2mind.net.*;
import java.io.Serializable;
public class ConorsMind implements Mind
{
int [][] surroundings = new int [12][16];
Coordinates [] ords = new Coordinates [192];
int currentX;
int currentY;
//====== Mind must respond to these methods: ==========================================================
// newrun(), endrun()
// getaction()
//======================================================================================================
public void newrun() throws RunError
{
}
public void endrun() throws RunError
{
}
private void formTwoDimmensional(int [] someArray)
{
int counter = 0;
int n=0;
for(int i = 0; i < 15; i++)
{
for(int z = 0; z < 12; z++)
{
surroundings[z][i] = someArray[counter];
if(surroundings[z][i] ==0) {
currentX=z;
currentY=i;
}
else if(surroundings[z][i]==4){
ords[n]= new Coordinates(z,i);
n++;
}
System.out.print(z+" , "+i+": "+surroundings[z][i]);
System.out.println();
counter++;
}
}
}
public Action getaction ( State state )
{
String s = state.toString();
String[] x = s.split(",");
int act =MinerWorldUpdated.NO_ACTIONS;
int counter = 0;
int [] surround = new int [192];
//in this way user will have ability to see what surrounds him
for(int i = 11; i < 203; i++)
{
surround[counter] = Integer.parseInt(x[i]);
counter++;
}
formTwoDimmensional(surround);
int [] response = new int [x.length];
for(int i = 0; i < x.length; i++)
{
response[i] = Integer.parseInt ( x[i] );
}
System.out.println("Current position: "+currentX+" ,"+currentY);
int coalX=ords[0].getX();
int coalY=ords[0].getY();
System.out.println("Coal position: "+coalX+" ,"+coalY);
if(coalX != 0 && coalY !=0)
{
if(coalX>currentX)
{
act=MinerWorldUpdated.ACTION_DOWN;
}
else if(coalY<currentY)
{
act=MinerWorldUpdated.ACTION_LEFT;
}
else if(coalX<currentX)
{
act=MinerWorldUpdated.ACTION_DOWN;
}
else if(coalY<currentY)
{
act=MinerWorldUpdated.ACTION_LEFT;
}
}
String a = String.format ( "%d", act );
return new Action ( a );
}
}
class Coordinates implements Serializable
{
private int x;
private int y;
public Coordinates(int x1, int y1)
{
x=x1;
y=y1;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
Error is as follows:
java.lang.NullPointerException
at ConorsMind.getaction(ConorsMind.java:146)
The error is stemming from the following two lines:
int coalX=ords[0].getX();
int coalY=ords[0].getY();
I am calling formTwoDimensional() and its working perfectly, the ords objects are being created successfully and are not null as testing with System.out.println(ords[n].getX()) is printing the expected result when placed in my else if(surroundings[z][i]==4) block.
You need to make sure that you're calling formTwoDimensional(). If you are indeed, then it's likely that you're not ever getting into your else if block in the nested for loop, and hence ords[0] is never actually being set, so when you try to access it, it's null.
The other thing to do, if you don't want to post the rest of your code, is to add some more debugging code. See below the boolean zero_pos_set. But make sure that you see the print "Zero pos set" before your program crashes. My bet is that you don't.
public class ConorsMind implements Mind
{
int [][] surroundings = new int [12][16];
Coordinates [] ords = new Coordinates [192];
boolean zero_pos_set = false;
private void formTwoDimmensional(int [] someArray)
{
int counter = 0;
int n=0;
for(int i = 0; i < 15; i++) {
for(int z = 0; z < 12; z++) {
surroundings[z][i] = someArray[counter];
if(surroundings[z][i] ==0) {
currentX=z;
currentY=i;
} else if(surroundings[z][i]==4) {
zero_pos_set = true;
ords[n]= new Coordinates(z,i);
n++;
}
counter++;
}
}
}
public Action getaction ( State state ) {
if(zero_pos_set) {
System.out.println("Zero pos set!");
}
int coalX=ords[0].getX();
int coalY=ords[0].getY();
System.out.println("Coal position: "+coalX+" ,"+coalY);
return new Action ( a );
}
}
Based on all of the debugging information posted within this thread, it seems that in your getaction() function, you're being passed some state, that doesn't contain 4.
When you parse this information and pass it to formTwoDimensional(), you will never reach the else if block, and so ords[0], or any other ords[n], will never be set.
As a result, when you try to access ords[0] back in your getaction() function, you actually get null, and hence your NullReferenceException.
It's an order of operations issue.
If you never make a call to formTwoDimmensional(), you'll never initialize anything inside of your array. Be sure you're calling that first.
The actual NPE happens when you attempt to call coalX=ords[0].getX();, which won't work if ords[0] is null.