Игра в поиск слов. Как искать в сетке и выделять результат? - PullRequest
0 голосов
/ 16 сентября 2018

Я новичок в программировании и не имею опыта работы с JavaScript, и у меня есть задание для моей школы. Мне нужно создать игру для поиска слов, в которой есть форма / кнопка поиска, которая находит слово и выделяет его в сетке. Я создал макет HTML / CSS, но я застрял, связывая его с JavaScript. Я хочу иметь возможность искать слова "BANGKOK", "LONDON", "SINGAPORE", "HAVANA" И "KYOTO", но я могу искать / выделять только одну букву / ячейку сетки, и я не не знаю, как сделать строку из букв из сетки, чтобы я мог найти эти слова. Я застрял здесь. На самом деле пытается понять. Может кто-нибудь помочь мне / направить / объяснить мне, как это работает? Я прочитал много вещей, но не могу найти то, что я ищу. Я был бы очень признателен. Мне также нужно, чтобы выделенное слово оставалось выделенным, когда оно найдено.

Вот что у меня есть: https://jsfiddle.net/fwg8hequ/10/

function search() {
  var text = document.getElementById("query").value;
  var query = new RegExp("(\\b" + text + "\\b)", "gim");
  var e = document.getElementById("searchtext").innerHTML;
  var enew = e.replace(/(<span>|<\/span>)/igm, "");
  document.getElementById("searchtext").innerHTML = enew;
  var newe = enew.replace(query, "<span>$1</span>");
  document.getElementById("searchtext").innerHTML = newe;

}
@charset "UTF-8";

/* CSS Document */

@font-face {
  font-family: 'RobotoSlab';
  src: url('RobotoSlab-bold.ttf');
}

@font-face {
  font-family: 'RobotoMono';
  src: url('RobotoMono-Regular.ttf');
}

.container {
  position: relative;
  width: 1000px;
  height: 800px;
  background: #ffcc78;
}

.header {
  position: absolute;
  left: 24.7%;
  right: 26%;
  top: 5.25%;
  bottom: 86.75%;
  overflow: auto;
}

.header img {
  width: 58px;
  height: 58px;
  left: 247px;
  top: 46px;
  float: left;
}

.header h1 {
  left: 33.8%;
  right: 28.4%;
  width: 378px;
  height: 64px;
  font-family: RobotoSlab;
  font-style: normal;
  line-height: normal;
  font-size: 48px;
  letter-spacing: -1px;
  color: #E25C5C;
  line-height: 5.28%;
  float: right;
}

form {
  position: absolute;
  left: 24.7%;
  right: 26%;
  top: 18.75%;
  bottom: 75%;
}

input[type=text] {
  float: left;
  left: 24.7%;
  right: 35.8%;
  top: 18.75%;
  bottom: 75%;
  width: 410px;
  height: 50px;
  background: #FFFFFF;
  border: 1px solid #417505;
  box-sizing: border-box;
  border-radius: 5px;
}

button {
  position: absolute;
  left: 66.5%;
  right: 27.3%;
  top: 18.75%;
  bottom: 75%;
  background: linear-gradient(180deg, #76AD0C 0%, #417505 100%);
  border-radius: 5px;
  font-family: RobotoSlab;
  font-style: normal;
  line-height: normal;
  font-size: 15px;
  color: #FFFFFF;
  float: right;
}

.grid-container {
  display: grid;
  grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
  background-color: #E25C5C;
  position: absolute;
  left: 24.7%;
  right: 26%;
  top: 30.5%;
  bottom: 7.88%;
  padding: 2px;
  border-radius: 5px;
}

.grid-item {
  background-color: #ffcc78;
  border: 2px solid #E25C5C;
  left: 26.2%;
  right: 27.2%;
  font-family: RobotoMono;
  line-height: 36px;
  font-size: 36px;
  letter-spacing: 2.9px;
  font-style: normal;
  font-weight: normal;
  text-align: center;
  padding: 2px;
}

#searchtext span {
  background-color: #F5A623;
}
<div class="container">

  <div class="header">
    <img src="icon.png" alt="Icon" height="58" width="58">

    <h1>WORD SEARCH</h1>

  </div>



  <form>
    <input name="query" id="query" type="text">
  </form>
  <button type="button" onClick="search();">SEARCH</button>



  <div class="grid-container" id="searchtext">

    <div class="grid-item">W</div>
    <div class="grid-item">S</div>
    <div class="grid-item">I</div>
    <div class="grid-item">A</div>
    <div class="grid-item">L</div>
    <div class="grid-item">C</div>
    <div class="grid-item">E</div>
    <div class="grid-item">O</div>
    <div class="grid-item">I</div>
    <div class="grid-item">V</div>

    <div class="grid-item">V</div>
    <div class="grid-item">A</div>
    <div class="grid-item">L</div>

    <div class="grid-item">B</div>
    <div class="grid-item">A</div>
    <div class="grid-item">N</div>
    <div class="grid-item">G</div>
    <div class="grid-item">K</div>
    <div class="grid-item">O</div>
    <div class="grid-item">K</div>


    <div class="grid-item">U</div>

    <div class="grid-item">T</div>
    <div class="grid-item">L</div>
    <div class="grid-item">O</div>
    <div class="grid-item">N</div>
    <div class="grid-item">D</div>
    <div class="grid-item">O</div>
    <div class="grid-item">N</div>
    <div class="grid-item">O</div>
    <div class="grid-item">I</div>


    <div class="grid-item">U</div>

    <div class="grid-item">S</div>
    <div class="grid-item">I</div>
    <div class="grid-item">N</div>
    <div class="grid-item">G</div>
    <div class="grid-item">A</div>
    <div class="grid-item">P</div>
    <div class="grid-item">O</div>
    <div class="grid-item">R</div>
    <div class="grid-item">E</div>


    <div class="grid-item">A</div>
    <div class="grid-item">L</div>
    <div class="grid-item">C</div>
    <div class="grid-item">O</div>
    <div class="grid-item">G</div>
    <div class="grid-item">E</div>
    <div class="grid-item">E</div>
    <div class="grid-item">U</div>
    <div class="grid-item">V</div>
    <div class="grid-item">R</div>

    <div class="grid-item">H</div>
    <div class="grid-item">A</div>
    <div class="grid-item">V</div>
    <div class="grid-item">A</div>
    <div class="grid-item">N</div>
    <div class="grid-item">A</div>
    <div class="grid-item">T</div>
    <div class="grid-item">L</div>
    <div class="grid-item">A</div>
    <div class="grid-item">A</div>

    <div class="grid-item">A</div>
    <div class="grid-item">B</div>
    <div class="grid-item">I</div>
    <div class="grid-item">S</div>
    <div class="grid-item">S</div>
    <div class="grid-item">N</div>
    <div class="grid-item">O</div>
    <div class="grid-item">R</div>
    <div class="grid-item">I</div>
    <div class="grid-item">S</div>

    <div class="grid-item">N</div>
    <div class="grid-item">K</div>
    <div class="grid-item">Y</div>
    <div class="grid-item">O</div>
    <div class="grid-item">T</div>
    <div class="grid-item">O</div>
    <div class="grid-item">A</div>
    <div class="grid-item">H</div>
    <div class="grid-item">B</div>
    <div class="grid-item">E</div>

    <div class="grid-item">Z</div>
    <div class="grid-item">M</div>
    <div class="grid-item">P</div>
    <div class="grid-item">T</div>
    <div class="grid-item">R</div>
    <div class="grid-item">E</div>
    <div class="grid-item">S</div>
    <div class="grid-item">J</div>
    <div class="grid-item">R</div>
    <div class="grid-item">L</div>

    <div class="grid-item">F</div>
    <div class="grid-item">P</div>
    <div class="grid-item">E</div>
    <div class="grid-item">K</div>
    <div class="grid-item">T</div>
    <div class="grid-item">A</div>
    <div class="grid-item">M</div>
    <div class="grid-item">L</div>
    <div class="grid-item">O</div>
    <div class="grid-item">J</div>


  </div>






</div>

1 Ответ

0 голосов
/ 17 сентября 2018

Давайте попробуем разобрать, что делает ваша функция поиска:

 function search() {
    // get the searched text OK
    var text = document.getElementById("query").value;

    // make a regexp out of the searched text OK
    var query = new RegExp("(\\b" + text + "\\b)", "gim");

    // retrieve the html content of the grid items's container OK
    var e = document.getElementById("searchtext").innerHTML;

    // remove all the spans tags from this html content (span tags in #searchtext are red)
    var enew = e.replace(/(<span>|<\/span>)/igm, "");

    // set the html stripped from the span tags as the content of #searchtext
    document.getElementById("searchtext").innerHTML = enew;

    // in the html stripped from span, wrap with spans all contents matching the search string
    var newe = enew.replace(query, "<span>$1</span>");

    // set the final html as the content of #searchtext
    document.getElementById("searchtext").innerHTML = newe;

}

Итак, сначала вы извлекаете HTML-код и пытаетесь найти текст в этом HTML-коде. Но так как вы сохраняете большинство тегов (вы удаляете только интервалы), вы не сможете найти текст только в содержимом ваших элементов div (ваш поиск будет загрязнен самими тегами div).

Мы могли бы делать сложные вещи с заменой, но должен быть другой путь.

Давайте теперь разберем проблему под рукой: мы хотели бы написать функцию, которая выделяет искомое слово в сетке в соответствии с правилами этой игры в словаре поиска (по горизонтали, по диагонали по вертикали).

`function highlightSearchedWord() {....}`

Для этого нет встроенной функции javascript, поэтому мы должны разделить проблему.

function highlightSearchedWord() {
    var text = getSearchedWord();
    highlightText(text);
}

мы можем решить getSearchedWord:

function getSearchedWord() {
    var text = document.getElementById("query").value;
    return text;
}

Теперь в highlightText нам нужно найти слово, которое способно читать буквы по заданным позициям в сетке, сравнивать их с искомым текстом, сохранять список позиций, если слово найдено, и выделять эти позиции.

Положение в сетке можно рассматривать как координаты x (индекс столбца буквы) и y (индекс строки буквы).

В javascript мы можем определить структурированные объекты с фигурными скобками {}, поэтому, например, позиция 0,0 (первая буква первой строки сетки) будет { x: 0, y: 0}

Первая буква вашей сетки находится в первом div (.grid-item) вашей сетки. Javascript дает вам возможность извлекать элементы на основе их имени класса.

`document.getElementsByClassName()`

Документация getElementsByClassName

Таким образом, мы можем составить список всех ваших элементов сетки, написав var items= document.getElementsByClassName('grid-item');

Позволяет определить функцию getItems:

function getItems() {
    var items= document.getElementsByClassName('grid-item');
    return items;
}

Отсюда мы можем легко получить новую функцию:

function getLetterAtPos(pos) {
    var items = getItems();
    // items is an array so we have to convert position {x, y} to index
    return items[posToIndex(pos)].innerHTML;
}

с индексом posToIndex:

function posToIndex(pos) {
    // if the grid is 10x10 the first element of first row is index 0 (0 * 10 + 0)
    // !remember first indice is 0!
    // the first item of second row is index 10 (1 * 10 + 0)
    // the second item of the third row is index 21 (1 * 10 + 1)
    return pos.y * 10 + pos.x;
}

Я собираюсь идти быстрее, чтобы ограничить размер ответа, но комментарии должны помочь.

Также может быть полезен способ выделить позицию:

Сначала определите класс css, делая выделение (проще добавить или удалить класс из элемента, чем обернуть / развернуть его содержимое в промежутке):

CSS:

.highlight {
    background-color:#F5A623;
}

Тогда вспомогательные функции javascript

ЯШ:

function addClass(elem, className) {
    // HTMLElement.className is a string with one or several class names separated by a space
    var classNames =  elem.className.split(" ");
    // we search the array classNames with indexOf to check if the class needs to be added
    if (classNames.indexOf(className) == -1) {
        // the class name is not found in the existing class names of this element so we just concatenate className to t elem.className
        elem.className += " " + className;
    }
}

function removeClass(elem, className) {
    // same as above we split elem.className into an array of classNames
    var classNames = elem.className.split(" ");
    // we search for index of the className we want to remove
    // index === -1 means not found, otherwise the index is the position of className in classNames
    var index = classNames.indexOf(className);
    if (index !== -1) {
        // javascript's version of remove at, splice(index, 1) means remove one item at index
        classNames.splice(index, 1);
        // join(' ') re concatenate classNames into a string of space separated class names
        elem.className = classNames.join(' ');
    }
}

function highlightPos(pos) {
    var item = getItems()[posToIndex(pos)];
    addClass(item, 'highlight');
}

// to reset highlights between searches
function clearHighlights() {
    var items = getGridItems();
    for (var i = 0; i < items.length; i++) {
        removeClass(items[i], 'sel');
    }
}

Ссылки:

split , indexOf и splice

Теперь, чтобы прочитать слова в сетке, мы должны найти как минимум позиции первой буквы искомого текста, а затем попытаться сопоставить каждую букву искомого текста:

function findLetterPositions(letter) {
    // we define a new array to receive our results
    var positions = [];
    // there are 10 columns x 10 rows of items
    var itemCount = 10 * 10;
    for (let i = 0; i < itemCount; i++) {
        var pos = indexToPos(i);
        // we compare letters lowercased
        if (getLetterAtPos(pos).toLowerCase() === letter.toLowerCase()) {
            // we have found letter at pos, so we add it to our array of positions (push)
            positions.push(pos);
        }
    }
    return positions;
}

с indexToPos, определяемым как обратная операция posToIndex (принимает индекс, возвращает pos):

function indexToPos(index) {
    var y = Math.floor(index / columnCount);
    var x = index - y * columnCount;
    return { x: x, y: y };
}

Для каждой найденной позиции нам нужно будет попытаться сопоставить каждую букву искомого текста, начиная с этой позиции и в указанном направлении. Например, вправо (с учетом начальной позиции первой буквы):

function tryAndMatchRight(text,initialPos) {

    var x = initialPos.x;
    var y = initialPos.y;
    var columnCount = 10;
    // we need to check that we are far enough from the edge of the grid for the whole word to fit, otherwise give up by returning
    if (x + text.length > columnCount) {
        return;
    }
    // word found == true by default, the for loop below will try to prove otherwise
    var wholeWordFound = true;
    // we will keep track of the letter positions we're trying
    var wordPositions = [];
    // obviously
    wordPositions.push(initialPos);
    // we will try each letter of text starting from the second (index 1) to the end of text (index length-1)
    for (var x2 = 1; x2 < text.length; x2++) {
        // building the position object for the current letter
        var pos = { x: x + x2, y: y};
        // if the comparaison fails we can stop
        if (text[x2].toLowerCase() !== getLetterAtPos(pos).toLowerCase()) {
            wholeWordFound = false;
            break;
        }
        wordPositions.push(pos);
    }
    if (wholeWordFound) {
        highLightPositions(wordPositions);
    }
}

function hightlightPositions(positions) {
    for(var i = 0; i < positions.length; i++) {
        highlightPos(positions[i]);
    }
}

Подводя итог, функция, вызываемая при нажатии кнопки поиска, может быть:

function search() {
    clearHighlights();
    var text = getSearchedText();
    var firstLetterPositions = findLetterPositions(text[0]);
    for (var i = 0; i < firstLetterPositions.length; i++) {
        var initialPos = firstLetterPositions[i];
        tryAndMatchRight(text,initialPos);
        // we only did it rightward, but other directions need their own functions
        // tryAndMatchDown(text,initialPos); 
        // tryAndMatchDownRight(text,initialPos);
        // tryAndMatchUpRight(text,initialPos);
    }
}

Полностью рабочий раствор, как скрипка здесь

Если вы действительно хотите разбираться в программировании, надеюсь, вы оцените этот ответ, демонстрирующий, что программирование в основном сводится к разделению больших проблем на более мелкие, пока проблемы не будут легко решаемы (и понятны) инструментами, предоставляемыми самим языком.

Мне все равно было весело! Приветствия

(PS: как указано Evochrome в комментариях ниже, две вспомогательные функции addClass и removeClass уже решаются простым js таким образом element.classList.add("mystyle") и element.classList.remove("mystyle"))

...