Это туториал по созданию интерактивного приложения для решения задачи о ходе коня на языках processing и p5.js
Создадим коня — прямоугольник rect(). Коня обозначим серым кругом
Пускай конь закрашивает все клетки по которым проходит, вот как здесь.
Далее, пусть конь притягивается к центру клетки при отпускании кнопки mouseRealised().
Добавим переменные storX и storY, которые будут хранить координаты клетки, на которой находится курсор. Если нажата левая кнопка и курсор находится над конём, то сохраняем координаты текщей клетки mod в переменные storX и storY:
При отпускании кнопки координаты storX и storY загружаются в координаты коня knightX и knightY.
Если для состояния «нажатая кнопка» существует стандартная булевая переменная mousePressed, то для состояния «ненажатая кнопка» такой переменной не существует — создадим её сами:
Теперь, если кнопка мыши не нажата (т.е. bool_mouseReleased=true) закрашиваем клетку с координатами storX и storY
Проверить можно здесь
Добавим кнопку отмены хода.
Пример, иллюстрирующий работу кнопок, находится здесь.
Сперва создадим списки IntList координат клеток, по которым прошел конь; нарисуем саму кнопку в левом нижнем углу:
Инициализируем списки
В конце очередного хода (при отпускании кнопки мыши) добавляем в список/стек координаты клетки:
Создадим булевую функцию overButton(), которая возвращает true, если курсор мыши находится над кнопкой и функцию buttonUpdate(), которая обновляет переменную
boolButton
Переменную и списки выводим в консоль в основном цикле программы:
Проверить можно здесь
Добавим функцию прыжка на предыдущую клетку при нажатии на кнопку отмены хода.
В функции mousePressed() проверяем, не нажата ли кнопка отмены хода. Если кнопка нажата, то удаляем последние элементы из списков координат (верхушки стеков), если в списке больше одного элемента:
При нажатии кнопки отмены хода в методе mouseClick() класса Module сохраняем в переменные storX и storY посление элементы списков координат (верхушек стеков):
При отпускании кнопки отмены хода возращаемся на предыдущую координату и возвращаем клетке изначальный (чёрный) цвет:
В функции mouseReleased() помещаем нашего коня в соотствующую клетку поля.
Теперь программа на языке processing работает корректно, а функция отмены хода в программе на p5.js работает не корректно.
Проверить программу на p5.js можно здесь
Заменим в основной программе списки InList массивами int[]. В массив, также как и в список, можно динамически добавлять элемент функцией append()
Извлекать элемент функцией pop() из массива нельзя. Для этого существует функция уменьшения длины массива на единицу shorten(). Для определения длины массива вместо size() надо использовать length
Эту программу можно добавить в виде js-скрипта к html-странице. Для запуска скрипта надо использовать processing.js
Посмотреть эту страницу можно здесь.
А здесь то же самое, но с изображением коня.
А вот здесь конь может ходить только буквой Г, остальные ходы запрещены.
Ссылка на github с текстами программ, представленных в статье.
Часть 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 с текстами программ, представленных в статье.
