Knight's Tour GUI in Processing

2019-08-10 01:18发布

问题:

I am working on a knight's tour problem with basic gui,i want to take user input in the two text fields which make up the (x,y) from user and then in one text box i print if solution is posiible and in the other i write the path adopted by the knight.My algorithm works fine and i have problem in gui.i have given a few default values to (x,y) for that i get correct output .But when i change the value of (x,y) in the textfield,no change occurs.this is the main file ,there is another event handler file,which is below it.Your help will be sincerely appreciated.I am working on processing 2.2.1.This is how the output screen looks like

Main file/project.pde

// Need G4P library
import g4p_controls.*;
Maxim maxim;
AudioPlayer player;
int x=-1;
int y=-1;
String solution="";
PImage img;
int count=0;

public void setup(){
  size(480, 320, JAVA2D);
  maxim=new Maxim(this);
  player=maxim.loadFile("song.wav");
  player.setLooping(true);

  img=loadImage("chess.jpg");

  createGUI();
  customGUI();

  // Place your setup code here

}
int t[]=new int[25];
boolean visited[][]=new boolean[5][5];
int check[][]=new int[5][5];
boolean b;
int counter=-1;
boolean move(int x,int y , int m){
 boolean result=false; 
   if (x<0 || x>=5 || y<0 || y>=5 || visited[x][y]==true)
   {
        return false;
   }

    visited[x][y]=true;

    if (m==24)
    {

        visited[x][y]=true;

        return true;
    }
    else
    {
      String xstring=String.valueOf(x);
      String ystring=String.valueOf(y);
      solution=solution+xstring+","+ystring+"  ";
        print (x);
        print(",");
        print(y);
        check[x][y]=counter+1;
        if (move(x+2,y+1,m+1) || move(x+2,y-1,m+1)
            || move(x-2,y+1,m+1) || move(x-2,y-1,m+1)
            || move(x+1,y+1,m+1) || move(x+1,y-1,m+1)
            || move(x-1,y+1,m+1) || move(x-1,y-1,m+1)){
            print (x);
            print(",");
            print(y);
            //check[x][y]=1;
            return true;

            }


    return false;


}
}


public void draw(){
 counter=counter+1; 
   background(0,128,128);
   image(img,0,0,480,320);
   player.play();
   textarea2.setText(solution);


   String txt1 = textfield1.getText();
   x = Integer.parseInt(txt1);

   String txt2 = textfield2.getText();
   y= Integer.parseInt(txt2);
   print(solution);
   if(x>=0 && y>=0)
   {
     b=move(x,y,0);


     if(b==false)
     {
       textarea1.setText("Solution is not possible,enter other coordinates");
     }
     if(b==true)
     {
       textarea1.setText("Congratulations solution is possible");
     }
   }
   if(count%8==0)
   {
     delay(1000);
     println(counter);

   }

}
void keyPressed()
{
  if (key==13)
  {
    solution="";
    print(solution);
    textarea2.setText(solution);
    String txt1 = textfield1.getText();
   x = Integer.parseInt(txt1);

   String txt2 = textfield2.getText();
   y= Integer.parseInt(txt2);
  }
    if(x>=0 && y>=0)
   {
     b=move(x,y,0);


     if(b==false)
     {
       textarea1.setText("Solution is not possible,enter other coordinates");
     }
     if(b==true)
     {
       textarea1.setText("Congratulations solution is possible");
     }
   }
}

// Use this method to add additional statements
// to customise the GUI controls
public void customGUI(){

}

this is the event handlers file

/* =========================================================
 * ====                   WARNING                        ===
 * =========================================================
 * The code in this tab has been generated from the GUI form
 * designer and care should be taken when editing this file.
 * Only add/edit code inside the event handlers i.e. only
 * use lines between the matching comment tags. e.g.

 void myBtnEvents(GButton button) { //_CODE_:button1:12356:
     // It is safe to enter your event code here  
 } //_CODE_:button1:12356:

 * Do not rename this tab!
 * =========================================================
 */

public void tf1(GTextField source, GEvent event) { //_CODE_:textfield1:418637:
  println("textfield1 - GTextField >> GEvent." + event + " @ " + millis());
} //_CODE_:textfield1:418637:

public void tf2(GTextField source, GEvent event) { //_CODE_:textfield2:859413:
  println("textfield2 - GTextField >> GEvent." + event + " @ " + millis());
} //_CODE_:textfield2:859413:

public void ta1(GTextArea source, GEvent event) { //_CODE_:textarea1:252891:
  println("textarea1 - GTextArea >> GEvent." + event + " @ " + millis());
} //_CODE_:textarea1:252891:

public void ta2(GTextArea source, GEvent event) { //_CODE_:textarea2:483845:
  println("textarea2 - GTextArea >> GEvent." + event + " @ " + millis());
} //_CODE_:textarea2:483845:

public void slider1_change1(GSlider source, GEvent event) { //_CODE_:slider1:280049:
  println("slider1 - GSlider >> GEvent." + event + " @ " + millis());
} //_CODE_:slider1:280049:

public void slider2_change1(GSlider source, GEvent event) { //_CODE_:slider2:362722:
  println("slider2 - GSlider >> GEvent." + event + " @ " + millis());
} //_CODE_:slider2:362722:



// Create all the GUI controls. 
// autogenerated do not edit
public void createGUI(){
  G4P.messagesEnabled(false);
  G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME);
  G4P.setCursor(ARROW);
  if(frame != null)
    frame.setTitle("Sketch Window");
  textfield1 = new GTextField(this, 210, 32, 160, 30, G4P.SCROLLBARS_NONE);
  textfield1.setText("1");
  textfield1.setPromptText("Enter x-Cordinate");
  textfield1.setOpaque(true);
  textfield1.addEventHandler(this, "tf1");
  textfield2 = new GTextField(this, 204, 96, 160, 30, G4P.SCROLLBARS_NONE);
  textfield2.setText("1");
  textfield2.setPromptText("Enter y Cordinate");
  textfield2.setLocalColorScheme(GCScheme.PURPLE_SCHEME);
  textfield2.setOpaque(true);
  textfield2.addEventHandler(this, "tf2");
  textarea1 = new GTextArea(this, 53, 196, 160, 80, G4P.SCROLLBARS_NONE);
  textarea1.setLocalColorScheme(GCScheme.GREEN_SCHEME);
  textarea1.setOpaque(true);
  textarea1.addEventHandler(this, "ta1");
  textarea2 = new GTextArea(this, 288, 192, 160, 80, G4P.SCROLLBARS_NONE);
  textarea2.setLocalColorScheme(GCScheme.YELLOW_SCHEME);
  textarea2.setOpaque(true);
  textarea2.addEventHandler(this, "ta2");
  slider1 = new GSlider(this, 96, 276, 264, 40, 10.0);
  slider1.setLimits(0.5, 0.0, 1.0);
  slider1.setNumberFormat(G4P.DECIMAL, 2);
  slider1.setOpaque(false);
  slider1.addEventHandler(this, "slider1_change1");
  slider2 = new GSlider(this, 348, 240, 100, 36, 10.0);
  slider2.setLimits(0.5, 0.0, 1.0);
  slider2.setNumberFormat(G4P.DECIMAL, 2);
  slider2.setOpaque(false);
  slider2.addEventHandler(this, "slider2_change1");
}

// Variable declarations 
// autogenerated do not edit
GTextField textfield1; 
GTextField textfield2; 
GTextArea textarea1; 
GTextArea textarea2; 
GSlider slider1; 
GSlider slider2; 

回答1:

There are some problems with above constructions. You do recursive move() method inside draw() method. But in Processing draw() is called many times in each second by animation thread.

Common desing for such cases is:

  • you should have variables that hold application state (logical state)
  • what your draw() method draws depends only on state
  • state may be modified by animation thread or by any other thread

I suggest changing your code a little:

First - state variables:

// 1 - display status, wait to enter values
// 2 - check if x and y are correct
// 3 - solve problem
int state=1;
boolean solutionExists=false;

public void setup() {
...
}

next draw() method:

void draw() {

   counter=counter+1; 
   background(0,128,128);

   String coords = "(" + x + "," + y + ")";

  if (state == 1) {
     if(solutionExists) {
       textarea1.setText("Congratulations solution is possible  " + coords);       
     } else {
       textarea1.setText("Solution is not possible,enter other coordinates  " +coords);
     }

     return; 
  }

  if (state == 2) {
    readXY();
    return;
  }

  if (state == 3) {
    println("find solution for: " + coords);
    solutionExists = move(x,y,0);
    state = 1;
    return;
  }

}

public void readXY() {
  try {
   x = Integer.parseInt(textfield1.getText().trim());
   y = Integer.parseInt(textfield2.getText().trim());
   state = 3;
  } catch(Exception e) {
    state = 1;
  }

}

and finally textfields handlers:

public void tf1(GTextField source, GEvent event) {
  if (event.getType().equals("LOST_FOCUS")) {
    state=2;
  }
}

public void tf2(GTextField source, GEvent event) {
  if (event.getType().equals("LOST_FOCUS")) {
    state=2;
  } 
}

As you can see:

  • if state==1 - draw() only updates message
  • if state==2 - draw() checks if x and y are valid, if valid -> change state to 3
  • if state==3 - draw() performs recursive algo, update solutionExists variable, change state to 1

Anytime when your textfield1 or textfield2 loose focus, it changes state to 2.

  • draw() is driven only by application state.
  • application state is modified by other events.

For best results, recursive algo should be performat in another thread, not in animation thread.

One fine note: when you edit textfield it may contains string such as "" (empty) or " 3" (leading space) or " 3 " etc - such text cannot be parsed with Integer.parseInt - you need to trim such text and be sure that NumberFormatException is not thrown - see readXY() method.