Создание графического приложения для решения задачи о ходе коня

  • Tutorial
Это туториал по созданию интерактивного приложения для решения задачи о ходе коня на языках processing и p5.js

Часть I


Создадим коня — прямоугольник rect(). Коня обозначим серым кругом

rect(bx, by, boxSize, boxSize);
  fill(50); // серый
  ellipse(bx+50,by+50,20,20); //  круг

Пускай конь закрашивает все клетки по которым проходит, вот как здесь.

Далее, пусть конь притягивается к центру клетки при отпускании кнопки mouseRealised().

Добавим переменные storX и storY, которые будут хранить координаты клетки, на которой находится курсор. Если нажата левая кнопка и курсор находится над конём, то сохраняем координаты текщей клетки mod в переменные storX и storY:

  void mouseClick() {
   if (mouseX >= x && mouseX <= x+100 && 
      mouseY >= y && mouseY <= y+100) {
   if (overBox && mousePressed && (mouseButton == LEFT)) {
    storX=x; // сохраняем x
    storY=y; // сохраняем y
          } 
        }
  }


При отпускании кнопки координаты storX и storY загружаются в координаты коня knightX и knightY.
  knightX=storX;
  knightY=storY; 

Если для состояния «нажатая кнопка» существует стандартная булевая переменная mousePressed, то для состояния «ненажатая кнопка» такой переменной не существует — создадим её сами:
 boolean bool_mouseReleased;
// ...
void mouseReleased() {
  bool_mouseReleased=true;
  locked = false;
  knightX=storX;
  knightY=storY;
}

Теперь, если кнопка мыши не нажата (т.е. bool_mouseReleased=true) закрашиваем клетку с координатами storX и storY
if((bool_mouseReleased ) && (x==storX && y==storY )){
        modColor=200;  }


Притягивание коня к центру клетки
// объявляем bool_mouseReleased; storX; storY;
boolean bool_mouseReleased;
float storX;
float storY;

float knightX;
float knightY;
// size of canvas 600*600
int edgeOfCanvas=600;
int knightSize = 100;
boolean overKnight = false;
boolean locked = false;
float xOffset = 0.0; 
float yOffset = 0.0; 
int unit = 100; // -> width / unit;
int unitSize=100; 
int count;
         
Module[] mods;

void setup() {
size(600, 600);
 knightX = 0;
  knightY = 0;
 rectMode(CORNER);
 stroke(100);
 
 int wideCount = edgeOfCanvas / unit;
  int highCount = edgeOfCanvas / unit;
  count = wideCount * highCount;
  mods = new Module[count];
  int index = 0;
  for (int y = 0; y < highCount; y++) {
    for (int x = 0; x < wideCount; x++) {
      mods[index++] = new Module(x*unit, y*unit);
     }
   }
}
void draw() {
  background(0); 
  for (Module mod : mods) {
    mod.mouseClick();
     mod.update();
  }
 //      //       //       //
 // Test if the cursor is over the box 
 fill(200);
  if (mouseX > knightX && mouseX < knightX+knightSize && 
      mouseY > knightY && mouseY < knightY+knightSize) {
    overKnight = true;  
  } else {
     overKnight = false;  
        }
  fill(200);
  rect(0,0,100,100); 
  rect(knightX, knightY, knightSize, knightSize);
  fill(50);
  ellipse(knightX+50,knightY+50,20,20);
 }
  
class Module {
  int x;
  int y;
 int modColor=0;
  // Contructor
  Module(int xT, int yT){
    x = xT;
    y = yT;
  }
  void mouseClick() {
   if ((mouseX >= x && mouseX <= x+100 && 
      mouseY >= y && mouseY <= y+100)&& 
    (overKnight && mousePressed && (mouseButton == LEFT))) {
    storX=x;
    storY=y; 
               }        
   if( (bool_mouseReleased ) && (x==storX && y==storY ) ){
        modColor=200;  }
  }
 void update() {
  fill(modColor);
  rect(x, y, unitSize, unitSize); 
   }
}

void mousePressed() {
  if(overKnight) { 
    locked = true;   
  } else {
    locked = false;
  }
  xOffset = mouseX-knightX; 
  yOffset = mouseY-knightY; 
}
void mouseDragged() {
  if(locked) {
    bool_mouseReleased=false;
    knightX = mouseX-xOffset; 
    knightY = mouseY-yOffset; 
  }
}
void mouseReleased() {
  bool_mouseReleased=true;
  locked = false;
  knightX=storX;
  knightY=storY;
}



Проверить можно здесь

Часть II


Добавим кнопку отмены хода.
Пример, иллюстрирующий работу кнопок, находится здесь.

Сперва создадим списки IntList координат клеток, по которым прошел конь; нарисуем саму кнопку в левом нижнем углу:

// list
IntList listOfCoordinatesX;
IntList listOfCoordinatesY;
//button
int buttonX=25, buttonY=525; 
int buttonSize = 50;     
boolean boolButton = false;

Инициализируем списки
listOfCoordinatesX = new IntList();
listOfCoordinatesY = new IntList();
  listOfCoordinatesX.append(0);
  listOfCoordinatesY.append(0);

В конце очередного хода (при отпускании кнопки мыши) добавляем в список/стек координаты клетки:
 if(overKnight){
  knightX=storX;
  knightY=storY; 
    listOfCoordinatesX.append(int(knightX));
    listOfCoordinatesY.append(int(knightY));
}

Создадим булевую функцию overButton(), которая возвращает true, если курсор мыши находится над кнопкой и функцию buttonUpdate(), которая обновляет переменную
boolButton
 void buttonUpdate() {
  if ( overButton(buttonX, buttonY, buttonSize, buttonSize) ) {
    boolButton = true;
  } else {
   boolButton = false;
  }
}
boolean overButton(int x, int y, int width, int height)  {
  if (mouseX >= x && mouseX <= x+width && 
      mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
}

Переменную и списки выводим в консоль в основном цикле программы:
  println(boolButton);
  println(listOfCoordinatesX);
  println(listOfCoordinatesY);


Программа целиком
// list
IntList listOfCoordinatesX;
IntList listOfCoordinatesY;
//button
int buttonX=25, buttonY=525; 
int buttonSize = 50;     
boolean boolButton = false;
//mouse
boolean bool_mouseReleased;
// jump to rect center on button release
float storX;
float storY;

float knightX;
float knightY;
// size of canvas 
int edgeOfCanvas=500;
int knightSize = 100;
boolean overKnight = false;
boolean locked = false;
float xOffset = 0.0; 
float yOffset = 0.0; 
int unit = 100; // -> width / unit;
int unitSize=100; 
int count;
         
Module[] mods;

void setup() {

size(500, 600);
stroke(100);
  knightX = 0;
  knightY = 0;
rectMode(CORNER);  
 listOfCoordinatesX = new IntList();
 listOfCoordinatesY = new IntList(); 
 listOfCoordinatesX.append(0); 
 listOfCoordinatesY.append(0);
 
 int wideCount = edgeOfCanvas / unit;
  int highCount = edgeOfCanvas / unit;
  count = wideCount * highCount;
  mods = new Module[count];
  int index = 0;
  for (int y = 0; y < highCount; y++) {
    for (int x = 0; x < wideCount; x++) {
      mods[index++] = new Module(x*unit, y*unit);
    }
   }
}
void draw() {
  background(0);
   buttonUpdate();
  for (Module mod : mods) {
    mod.mouseClick();
    mod.update();
  }
 //      //      //      //      //      //     
 // Test if the cursor is over the box 
 fill(200);
  if (mouseX > knightX && mouseX < knightX+knightSize && 
      mouseY > knightY && mouseY < knightY+knightSize) {
    overKnight = true;  
  } else {
     overKnight = false;  
        }
 fill(200);
 rect(0,0,100,100); 
  rect(knightX, knightY, knightSize, knightSize);
  fill(50);
  ellipse(knightX+50,knightY+50,20,20);
  // draw button
  rect(buttonX,buttonY,buttonSize,buttonSize);
  if(boolButton && mousePressed) { fill(200);
   rect(buttonX,buttonY,buttonSize,buttonSize); }   
  println();
  println(storX);
  println(storY);
  println(boolButton);
  println(listOfCoordinatesX);
  println(listOfCoordinatesY);  
 }


class Module {
  int x;
  int y;
 int modColor=0;
  // Contructor
  Module(int xT, int yT){
    x = xT;
    y = yT;
  }  
  void mouseClick() {
    if (mouseX >= x && mouseX <= x+100 && 
      mouseY >= y && mouseY <= y+100) {
   if (overKnight && mousePressed && (mouseButton == LEFT)) {
    storX=x;
    storY=y; 
  //  if(bool_mouseReleased ) {modColor=200;} 
     } 
    }
   if((bool_mouseReleased ) && (x==storX && y==storY )){
       modColor=200;  }
}
void update() {
  fill(modColor);
  rect(x, y, unitSize, unitSize); 
   }
}

void mousePressed() {
  if(overKnight) { 
    locked = true;    
  } else {
    locked = false;
   }
  xOffset = mouseX-knightX; 
  yOffset = mouseY-knightY; 
}
void mouseDragged() {
  if(locked) {
    bool_mouseReleased=false;
    knightX = mouseX-xOffset; 
    knightY = mouseY-yOffset; 
  }
}
void mouseReleased() {
  bool_mouseReleased=true;
  locked = false;
  if(overKnight){
  knightX=storX;
  knightY=storY;
      listOfCoordinatesX.append(int(knightX));
      listOfCoordinatesY.append(int(knightY)); 
                }
 }
// button
 void buttonUpdate() {
  if ( overButton(buttonX, buttonY, buttonSize, buttonSize) ) {
    boolButton = true;
  } else {
   boolButton = false;
  }
}
boolean overButton(int x, int y, int width, int height)  {
  if (mouseX >= x && mouseX <= x+width && 
      mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
}   



Проверить можно здесь

Часть III


Добавим функцию прыжка на предыдущую клетку при нажатии на кнопку отмены хода.
В функции mousePressed() проверяем, не нажата ли кнопка отмены хода. Если кнопка нажата, то удаляем последние элементы из списков координат (верхушки стеков), если в списке больше одного элемента:
if(boolButton && listOfCoordinatesX.size()>1)
{   listOfCoordinatesX.pop();
     listOfCoordinatesY.pop();   }

При нажатии кнопки отмены хода в методе mouseClick() класса Module сохраняем в переменные storX и storY посление элементы списков координат (верхушек стеков):
if(boolButton && mousePressed)
{   storX= listOfCoordinatesX.get(listOfCoordinatesX.size()-1);
     storY= listOfCoordinatesY.get(listOfCoordinatesY.size()-1);  } 

При отпускании кнопки отмены хода возращаемся на предыдущую координату и возвращаем клетке изначальный (чёрный) цвет:
if(boolButton && bool_mouseReleased){       
    if(x==storX && y==storY ) {   modColor=0;  }  
 }

В функции mouseReleased() помещаем нашего коня в соотствующую клетку поля.
if(boolButton)
  { knightX=storX;
     knightY=storY;
    }  


Полный код

// list
IntList listOfCoordinatesX;
IntList listOfCoordinatesY;
//button
int buttonX=25, buttonY=525; 
int buttonSize = 50;     
boolean boolButton = false;
//mouse
boolean bool_mouseReleased;
// jump to rect center on button release
float storX;
float storY;

float knightX;
float knightY;
// size of canvas 
int edgeOfCanvas=500;
int knightSize = 100;
boolean overKnight = false;
boolean locked = false;
float xOffset = 0.0; 
float yOffset = 0.0; 
int unit = 100; // -> width / unit;
int unitSize=100; 
int count;
         
Module[] mods;

void setup() {

size(500, 600);
stroke(100);
  knightX = 0;
  knightY = 0;
rectMode(CORNER);  
 listOfCoordinatesX = new IntList();
 listOfCoordinatesY = new IntList(); 
 listOfCoordinatesX.append(0); 
 listOfCoordinatesY.append(0);
 
 int wideCount = edgeOfCanvas / unit;
  int highCount = edgeOfCanvas / unit;
  count = wideCount * highCount;
  mods = new Module[count];
  int index = 0;
  for (int y = 0; y < highCount; y++) {
    for (int x = 0; x < wideCount; x++) {
      mods[index++] = new Module(x*unit, y*unit);
    }
   }
}
void draw() {
  background(0);
   buttonUpdate();
  for (Module mod : mods) {
    mod.mouseClick();
    mod.update();
  }
 //      //      //      //      //      //     
 // Test if the cursor is over the box 
 fill(200);
  if (mouseX > knightX && mouseX < knightX+knightSize && 
      mouseY > knightY && mouseY < knightY+knightSize) {
    overKnight = true;  
  } else {
     overKnight = false;  
        }
 fill(200);
 rect(0,0,100,100); 
  rect(knightX, knightY, knightSize, knightSize);
  fill(50);
  ellipse(knightX+50,knightY+50,20,20);
  // draw button
  rect(buttonX,buttonY,buttonSize,buttonSize);
  if(boolButton && mousePressed) { fill(200);
   rect(buttonX,buttonY,buttonSize,buttonSize); }
 /*  
  println();
  println(storX);
  println(storY);
  println(boolButton);
  println(listOfCoordinatesX);
  println(listOfCoordinatesY);
  */
 }


class Module {
  int x;
  int y;
 int modColor=0;
  // Contructor
  Module(int xT, int yT){
    x = xT;
    y = yT;
  }
  
  void mouseClick() {
    if (mouseX >= x && mouseX <= x+100 && 
      mouseY >= y && mouseY <= y+100) {
   if (overKnight && mousePressed && (mouseButton == LEFT)) {
    storX=x;
    storY=y; 
  //  if(bool_mouseReleased ) {modColor=200;} 
     } 
    }
   if((bool_mouseReleased ) && (x==storX && y==storY )){
       modColor=200;  }       
   if(boolButton && mousePressed){   
             storX= listOfCoordinatesX.get(listOfCoordinatesX.size()-1);
             storY= listOfCoordinatesY.get(listOfCoordinatesY.size()-1);              
             } 
  if(boolButton && bool_mouseReleased){       
    if(x==storX && y==storY ){
       modColor=0;  }  }      

}
void update() {
  fill(modColor);
  rect(x, y, unitSize, unitSize); 
   }
}

void mousePressed() {
  if(overKnight) { 
    locked = true; 
     // listOfCoordinatesX.append(int(knightX));
     // listOfCoordinatesY.append(int(knightY));    
  } else {
    locked = false;
   }
  xOffset = mouseX-knightX; 
  yOffset = mouseY-knightY; 
  if(boolButton && listOfCoordinatesX.size()>1){
     listOfCoordinatesX.pop();
      listOfCoordinatesY.pop();
               }
}
void mouseDragged() {
  if(locked) {
    bool_mouseReleased=false;
    knightX = mouseX-xOffset; 
    knightY = mouseY-yOffset; 
  }
}
void mouseReleased() {
  bool_mouseReleased=true;
  locked = false;
  if(overKnight){
  knightX=storX;
  knightY=storY;
  listOfCoordinatesX.append(int(knightX));
  listOfCoordinatesY.append(int(knightY)); 
                }
 if(boolButton){
        knightX=storX;
        knightY=storY;
              }                
 }
// button
 void buttonUpdate() {
  if ( overButton(buttonX, buttonY, buttonSize, buttonSize) ) {
    boolButton = true;
  } else {
   boolButton = false;
  }
}
boolean overButton(int x, int y, int width, int height)  {
  if (mouseX >= x && mouseX <= x+width && 
      mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
} 



Теперь программа на языке processing работает корректно, а функция отмены хода в программе на p5.js работает не корректно.
Проверить программу на p5.js можно здесь

Заменим в основной программе списки InList массивами int[]. В массив, также как и в список, можно динамически добавлять элемент функцией append()
 listOfCoordinatesX = append(listOfCoordinatesX,0);

Извлекать элемент функцией pop() из массива нельзя. Для этого существует функция уменьшения длины массива на единицу shorten(). Для определения длины массива вместо size() надо использовать length
Программа целиком
 // list
int[] listOfCoordinatesX;
int[] listOfCoordinatesY;
 
//button
int buttonX=25, buttonY=525; 
int buttonSize = 50;     
boolean boolButton = false;
//mouse
boolean bool_mouseReleased;
// jump to rect center on button release
float storX;
float storY;

float knightX;
float knightY;
// size of canvas 
int edgeOfCanvas=500;
int knightSize = 100;
boolean overKnight = false;
boolean locked = false;
float xOffset = 0.0; 
float yOffset = 0.0; 
int unit = 100; // -> width / unit;
int unitSize=100; 
int count;
         
Module[] mods;

void setup() {

size(500, 600);
stroke(100);
// создаём массивы
  listOfCoordinatesX = new int[0]; //надо указать размер массива
  listOfCoordinatesY = new int[0]; 
// добавляем координату (0,0)
   listOfCoordinatesX = append(listOfCoordinatesX,0);
   listOfCoordinatesY = append(listOfCoordinatesY,0);
  knightX = 0;
  knightY = 0;
rectMode(CORNER);  
 
 int wideCount = edgeOfCanvas / unit;
  int highCount = edgeOfCanvas / unit;
  count = wideCount * highCount;
  mods = new Module[count];
  int index = 0;
  for (int y = 0; y < highCount; y++) {
    for (int x = 0; x < wideCount; x++) {
      mods[index++] = new Module(x*unit, y*unit);
    }
   }
}
void draw() {
  background(0);
   buttonUpdate();
  for (Module mod : mods) {
    mod.mouseClick();
    mod.update();
  }
 //      //      //      //      //      //     
 // Test if the cursor is over the box 
 fill(200);
  if (mouseX > knightX && mouseX < knightX+knightSize && 
      mouseY > knightY && mouseY < knightY+knightSize) {
    overKnight = true;  
  } else {
     overKnight = false;  
        }
 fill(200);
 rect(0,0,100,100); 
  rect(knightX, knightY, knightSize, knightSize);
  fill(50);
  ellipse(knightX+50,knightY+50,20,20);
  // draw button
  rect(buttonX,buttonY,buttonSize,buttonSize);
  if(boolButton && mousePressed) { fill(200);
   rect(buttonX,buttonY,buttonSize,buttonSize); }   
// println();
// println(storX);
// println(storY);
//  println(boolButton);
//  println(listOfCoordinatesX);
//  println(listOfCoordinatesY);
  }


class Module {
  int x;
  int y;
 int modColor=0;
  // Contructor
  Module(int xT, int yT){
    x = xT;
    y = yT;
  }
  
  void mouseClick() {
    if (mouseX >= x && mouseX <= x+100 && 
      mouseY >= y && mouseY <= y+100) {
   if (overKnight && mousePressed && (mouseButton == LEFT)) {
    storX=x;
    storY=y; 
  //  if(bool_mouseReleased ) {modColor=200;} 
     } 
    }
   if((bool_mouseReleased ) && (x==storX && y==storY )){
       modColor=200;  }       
   if(boolButton && mousePressed){   
             storX= listOfCoordinatesX[listOfCoordinatesX.length-1];
             storY= listOfCoordinatesY[listOfCoordinatesY.length-1];              
             } 
  if(boolButton && bool_mouseReleased){       
    if(x==storX && y==storY ){
       modColor=0;  }  }      
  }
void update() {
  fill(modColor);
  rect(x, y, unitSize, unitSize); 
   }
}

void mousePressed() {
  if(overKnight) { 
    locked = true;      
  } else {
    locked = false;
   }
  xOffset = mouseX-knightX; 
  yOffset = mouseY-knightY; 
  if(boolButton && listOfCoordinatesX.length>1){
   // listOfCoordinatesX.pop();
   //  listOfCoordinatesY.pop();
    listOfCoordinatesX=shorten(listOfCoordinatesX);
    listOfCoordinatesY=shorten(listOfCoordinatesY);
      }
 }
void mouseDragged() {
  if(locked) {
    bool_mouseReleased=false;
    knightX = mouseX-xOffset; 
    knightY = mouseY-yOffset; 
  }
}
void mouseReleased() {
  bool_mouseReleased=true;
  locked = false;
  if(overKnight){
  knightX=storX;
  knightY=storY;
  listOfCoordinatesX=append(listOfCoordinatesX,int(knightX));
  listOfCoordinatesY=append(listOfCoordinatesY,int(knightY)); 
                }
 if(boolButton){
        knightX=storX;
        knightY=storY;
              }                
 }
// button
 void buttonUpdate() {
  if ( overButton(buttonX, buttonY, buttonSize, buttonSize) ) {
    boolButton = true;
  } else {
   boolButton = false;
  }
}
boolean overButton(int x, int y, int width, int height)  {
  if (mouseX >= x && mouseX <= x+width && 
      mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
} 


Эту программу можно добавить в виде js-скрипта к html-странице. Для запуска скрипта надо использовать processing.js
Посмотреть эту страницу можно здесь.
А здесь то же самое, но с изображением коня.
А вот здесь конь может ходить только буквой Г, остальные ходы запрещены.

Ссылка на github с текстами программ, представленных в статье.

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 0

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое