Создайте свой собственный Wordle для чисел: Numble

После того, как Wordle захватил мир и мою ленту в Твиттере, я, как и весь остальной мир, стал несколько одержим. Я стал настолько одержим, что меня осенила идея сделать приложение, похожее, но с упором на числа. Две недели спустя родился Numble — Wordle для чисел.

Намбл

Правила Намбла

Это действительно очень просто: угадать трехзначное число, кратное трем.

У вас есть четыре предположения, и после каждого предположения цвета каждой цифры меняются в зависимости от ее положения и от того, находится ли она на самом деле в Numble.

Зеленый: цифра в правильном месте.

Желтый: цифра есть в Numble, но не в том месте.

Серый: цифры вообще нет в Numble.

Если вы забыли из школы, правило, позволяющее узнать, является ли число кратным трем, заключается в том, что цифры в сумме кратны трем.

Например:

123 кратно трем, потому что 1 + 2 + 3 = 6.

Няня государство

Чтобы упростить сборку Numble, я использовал небольшую библиотеку под названием Nanny State. Это написано Дарреном Джонсом, если вы пропустили, он недавно написал статью, представляющую это. Он хранит все данные приложения в одном объекте с именем State, а затем автоматически перерисовывает HTML-представление в зависимости от любых изменений, внесенных в State. Благодаря своей скорости и эффективности, а также отсутствию нового синтаксиса, он удивительно прост и легок в освоении.

Прежде всего, нам нужно импортировать Nanny State и настроить State, View и Update.

Чтобы импортировать его, вам просто нужно скопировать эту строку кода в самую первую строку вашей программы:

import { Nanny, html } from 'https: //cdn.skypack.dev/nanny-state

Если вы хотите узнать немного больше о том, что вы импортируете, и о том, как работает структура всего, обязательно перейдите по ссылке выше. Далее нам нужно создать представление:

const View = state => html`

Numble`

Это View функция, которая возвращает строку, которая в основном представляет собой HTML-код, который будет отображаться на нашей странице. Это базовый макет для начала, и как только все будет на месте, должен появиться заголовок «Numble». Он принимает State в качестве параметра, предоставляя нам доступ к данным, хранящимся в одном объекте.

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

const State = {

View

}

И, наконец, нам нужно вызвать Nanny функцию. Это отобразит первоначальный вид. Мы также присваиваем переменной Update возвращаемое значение. Это позволит нам обновить состояние позже.

const Update = Nanny (State)

Ваша страница должна выглядеть так:

Начальная страница

Вот как должен выглядеть код в целом:

Теперь, когда Nanny State настроена, мы можем приступить к созданию игры, которая к концу должна стать полностью функционирующей игрой «Угадай число».

Научитесь программировать с помощью JavaScript — бесплатно

У каждого конца есть начало

Несколько вещей, прежде чем мы начнем. Одним из преимуществ View функции является то, что она использует литералы шаблонов, что означает, что мы можем добавлять логику в сам HTML. Это очень удобно, когда вы хотите включить различные представления на основе состояния. К сожалению, стандартный if оператор не будет работать, и вместо этого нам нужно будет использовать тернарные операторы.

Если вы еще не знакомы, тернарные операторы работают так же, как оператор if else: условие? что делать, если правда: что делать, если ложь. Короче говоря, обменяйте if на а? и else на:.

Например, вот отличный пример, который я увидел в Твиттере на днях:

const toDo = amIHungry? «🍰»: «😴»

Это эквивалентно:

if (amIHungry) {

const toDo = «🍰»

}

else{

const toDo = «😴»

}

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

Кнопка «Пуск» — отличный способ добавить некоторую структуру в игру, особенно такую, как Numble, поэтому для этого нам нужно добавить свойство в State, started. Значение started должно быть false, поскольку первое, что мы хотим, чтобы пользователь увидел, — это страница меню (которая на данный момент будет состоять из кнопки «Пуск» и заголовка).

State теперь должно выглядеть так:

const State = {

started: false,

start, finish,

View

}

Это View то, что здесь меняется больше всего, и мы можем использовать наш первый и основной тернарный оператор.

Обратите внимание, что он включает две переменные с именами start и finish. Это ссылки на обработчики событий, которые мы скоро напишем.

В состоянии няни на любые обработчики событий необходимо ссылаться внутри State объекта.

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

const View = state => html`

Numble

${state.started?

html`<button onclick=${state.finish}>END`

:

html`<button onclick=${state.start}>START`

}`

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

Он следует той же логике, что и приведенный выше пример cake/hungry: он проверяет, state.started является ли true. Если это так, отображается кнопка «Конец». Если нет, вместо этого отображается кнопка «Пуск».

Кнопка Пуск

Кнопка «Конец»

У самих кнопок есть встроенные прослушиватели событий: у кнопки «Пуск» есть тот, который вызывает функцию, start, а у кнопки «Конец» есть тот, который вызывает функцию finish. Очевидно, что этих функций еще не существует, что подводит нас к нашей следующей работе: созданию обработчиков событий.

Сначала мы выполним start функцию. Это очень просто, так как все, что нам нужно сделать, это обновить started свойство до true. Код должен выглядеть так:

const start = event => Update ({started: true})

Это использует Update функцию, которую мы написали ранее, и изменяет State так, что значение started теперь равно true. Когда это произойдет, представление будет повторно отображено, отображая кнопку «Конец» в результате нашего тернарного оператора.

Возможно, вы захотите попробовать написать finish обработчик событий самостоятельно, так как он работает почти так же, как и start функция, с той лишь разницей, что Update функция изменяет.

Вот как finish должна выглядеть функция:

const finish = event => Update ({started: false})

Удивительно! Теперь у вас есть самая скучная игра в мире!

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

Опять же, вот пример того, как должен выглядеть код:

Шаг 2: Генерация случайного числа

Теперь мы можем сделать нашу игру немного более захватывающей с помощью одного из самых важных аспектов Numble: самого числа.

Этот шаг включает в себя немного математики и логики, но на самом деле это не так уж сложно, если вы разберетесь с этим. Сама функция должна выглядеть так (ниже я объясню, что происходит):

const generateNumber = () => (3*Math.ceil (Math.random () *299+34)).toString ()

Это стрелочная функция, которая возвращает трехзначное число, кратное трем, в виде строки.

Глядя конкретно на Math.ceil (Math.random () *299+34), это генерирует случайное число от 1 до 299 с помощью Math.random () и округляет его с помощью Math.ceil. 34 добавляется, а затем это число умножается на три, чтобы убедиться, что число кратно трем между 102 и 999, т. е. 3-значное число, кратное 3, или «число».

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

Наша следующая задача — отображать число каждый раз, когда пользователь нажимает «старт».

Лучший способ сделать это — добавить свойство к объекту состояния с именем number. Однако нам не нужно делать это в оригинале State, нам просто нужно сделать это при нажатии кнопки «Пуск», то есть в start обработчике событий.

Это изменит нашу start функцию, чтобы она выглядела так:

const start = event => Update ({

started: true,

number: generateNumber ()

})

Значение нового свойства number— это возвращаемое значение функции, которую мы только что создали generateNumber (): случайное трехзначное число, кратное трем.

Чтобы отобразить это, нам нужно добавить строку в View, особенно в разделе HTML, когда state.started есть true, чтобы View теперь выглядело так:

const View = state => html`

Numble

${state.started?

html`

${state.number}

 

<button onclick=${state.finish}>END`

:

html`<button onclick=${state.start}>START`

}`

Все, что мы здесь сделали, это добавили a

со значением id, «number»которое отображает state.number случайно сгенерированное трехзначное число, кратное трем.

 

Если вы протестируете код сейчас, вы сможете видеть разные числа каждый раз, когда нажимаете кнопку «Старт», и если вы сложите цифры, вы обнаружите, что числа кратны трем!

Проверьте свой код на мой codepen:

Научитесь программировать с помощью JavaScript — бесплатно

Шаг 3: Входы и клавиатуры

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

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

Этот шаг состоит из трех основных частей:

Создать виртуальную клавиатуру

Показать предположение пользователя

Проверка правильности предположения пользователя

Несмотря на то, что все они зависят друг от друга, их гораздо легче понять, если разбить все на более мелкие фрагменты.

Во-первых, нам нужно добавить функции, которые мы собираемся использовать, и еще три свойства в State:

const State = {

started: false,

digits: Array (10).fill («grey»),

guess: Array (3).fill (null),

count: 0,

start, finish, remove, check, appear,

View

}

Просматривая их в таком порядке, значение digits теперь представляет собой массив длиной 10, где каждое пространство заполнено строкой «grey». Это потому, что мы будем использовать это, чтобы отслеживать, какого цвета должна быть каждая цифра в игре, а индекс массива будет представлять каждую возможную цифру от 0 до 9.

Начальное значение guess также является массивом длины 3, каждое пространство которого заполнено null.

И, наконец, count устанавливается на 0, и это будет использоваться для подсчета того, сколько цифр угадал игрок.

Мы собираемся сопоставить digits массив, чтобы создать нашу экранную клавиатуру, поэтому нам нужно добавить несколько вещей в файл View. Хотя, прежде чем мы это сделаем, нам нужно избавиться от

символа number, иначе это просто уничтожит весь смысл игры.

 

const View = state => html`

Numble

${state.started?

html`

 

${state.guess.map (number => html`

${number}
`) }

${state.digits.map ( (digit, index) => html`<button onclick=${appear (index) }>${index}`) }

<button onclick=${remove}>DELETE

<button onclick=${check}>ENTER

<button onclick=${finish}>END`

:

html`<button onclick=${start}>START`

}`

Вместо того,

что показывал number, теперь у нас есть два
s, один с id of «guesses» и один с id of «keyboard».

 

В «догадках»

у нас есть первая из многих.map () функций, и она отображает массив длины 3, создавая отдельный
элемент для каждого элемента в массиве, отображая элемент. Это означает, что в начале, когда значение всех элементов массива равно null, будут отображаться три пустых места.

 

Вот пример того, как это должно выглядеть (с моим CSS):

1 сетка

В конце концов, когда значение каждого элемента в массиве изменится, то, что отображается, также будет изменено, чтобы соответствовать.

А внутри «Клавиатуры»

у нас есть три вещи:

 

${state.digits.map ( (digit, index) => html`<button onclick=${state.appear (index) }>${index}`) }

Это отображает массив длиной 10, создавая кнопку для каждого элемента и отображая index каждый элемент. Другими словами, цифры от 0 до 9. Каждая кнопка также имеет встроенный прослушиватель событий, который вызывает обработчик событий appear и предоставляет index в качестве аргумента. Тем не менее, мы полностью изучим это через мгновение.

Затем у нас есть две кнопки, одна называется «Удалить», а другая — «Ввод». Обе они имеют встроенные прослушиватели событий, которые вызывают соответствующие обработчики событий remove и check. И снова мы полностью изучим их через мгновение.

Во-первых, это пример того, как может выглядеть ваша клавиатура:

Цифровая клавиатура

Глядя на appear обработчик событий, мы хотим, чтобы эта функция отображала цифру, которую игрок щелкает в первом пробеле guess.

const appear = guess => event => {

Update (state => ({

guess: state.guess.map ( (digit, index) => index === state.count? guess: digit),

count: state.count + 1

}))

}

Во-первых, единственная разница между этим обработчиком событий и теми, которые мы делали раньше, заключается в том, что эта функция имеет дополнительный параметр guess. Это массив index, digits который был предоставлен в качестве аргумента. Другими словами, это цифра, на которую нажал игрок.

Функция Update выглядит немного иначе. Это связано с тем, что ему нужен доступ к состоянию, поэтому он снабжен стрелочной функцией, которая отображает старое состояние в новое состояние (состояние няни называет эти «функции преобразователя»).

С точки зрения того, что оно на самом деле обновляет, guess свойство сопоставляется с исходным массивом из трех nulls, и если index элемент равен count (позиция предположения), значение null заменяется на guess (которое будет номером кнопку, которую нажал пользователь). Если index не равно count, значение элемента остается прежним: null.

Затем он увеличивается count на 1, позволяя пользователю ввести свое второе предположение во второе пространство.

Вот как будет выглядеть строка, когда пользователь щелкнет несколько чисел:

Заполненная сетка Numble

Обработчик remove события (по иронии судьбы) почти идентичен:

const remove = event => {

Update (state => ({

guess: state.guess.map ( (digit, index) => index === state.count — 1? null: digit),

count: state.count — 1

}))

}

Следуя логике appear функции, вы сможете понять, что здесь происходит, но не беспокойтесь, если это не так. Он обновляется guess путем сопоставления с исходным массивом, и если index равно предыдущему количеству догадок (т. е. счет — 1), он заменяет значение элемента на null, эффективно удаляя догадку.

И на этот раз он уменьшается count на единицу, позволяя пользователю продолжать делать предположения.

Просто check функция идти.

Обработчик события для кнопки «Ввод» называется check, и мы хотим, чтобы он (сюрприз) проверял правильность предположения пользователя, но мы также хотим, чтобы он сбрасывал предположение, чтобы пользователь мог повторить попытку.

Функция выглядит следующим образом:

const check = event => {

Update (state => {

const numble = state.guess.join («») === state.number

return {

feedback: numble? «NUMBLE!»: «Wrong!»,

guess: Array (3).fill (null),

count: 0

}

})

}

Как и раньше, Update использует функцию преобразования и принимает state в качестве параметра, что дает нам прямой доступ ко всем данным приложения, хранящимся в состоянии. Затем он создает логическую константу с именем numble. Это может выглядеть не так, но state.guess.join («») === state.number на самом деле это условие (проверяет, соответствует ли предположение пользователя сгенерированному нами числу), и если оно соответствует этому условию, значение numble будет, true, а если нет, то будет false.

Затем он возвращает три обновленных свойства состояния:

feedback принимает логическое значение, которое мы только что создали, и если это так, true оно устанавливает значение как строку «NUMBLE!» и если это так, false он устанавливает значение как строку «Неправильно!»

guess изменяется обратно на массив длины 3, заполненный null. Это фактически сбрасывает догадку пользователя, позволяя ему угадать снова.

count также сбрасывается в 0, что означает, что программа может работать, как будто начиная с самого начала.

Наш последний шаг — добавить немного HTML View, чтобы можно было отобразить обратную связь.

Лучше всего разместить его под догадкой и над клавиатурой. Итак, ваш финал View должен выглядеть примерно так:

const View = state => html`

Numble

${state.started?

html`

 

${state.guess.map (number => html`

${number}
`) }

${state.feedback

${state.digits.map ( (digit, index) => html`<button onclick=${state.appear (index) }>${index}`) }

<button onclick=${state.remove}>DELETE

<button onclick=${state.check}>ENTER

<button onclick=${state.finish}>END`

:

html`<button onclick=${state.start}>START`

}`

А при желании можно использовать feedback для установки сообщения при старте игры, например в start обработчик события можно добавить feedback свойство со строковым значением («Угадай 3 цифры»):

const start = event => {

Update ({

started: true,

number: generateNumber (),

feedback: «Guess 3 digits»

})

}

Вот и все! Теперь у вас есть полностью функционирующая игра «Угадай число»!

Прежде чем вы перейдете ко второй статье, есть всего пара замечаний о CSS и ошибках.

Если вы хотите добавить свой собственный CSS, это прекрасно, но если вы просто хотите сосредоточиться на коде, вы можете скопировать мой CSS из окончательной кодовой ручки:

Если вы хороший программист, вы, вероятно, сможете найти здесь некоторые ошибки, например, что, если пользователь нажмет «Ввод» до того, как угадает три цифры? И если вы начнете играть с этим, вы обязательно сможете заметить еще кое-что.

Их совсем не сложно исправить, вам просто понадобится пара условий, где это уместно. Например, чтобы решить проблему проверки до трех цифр, в check функции можно написать:

const check = event => {

Update (state => {

const numble = state.guess.join («») === state.number

return state.count < 3? {

feedback: «too short»

}

:

{

feedback: numble? «NUMBLE!»: «Wrong!»,

guess: Array (3).fill (null),

count: 0

}

})

}

Это просто проверяет, меньше ли количество догадок, чем 3, и соответственно возвращает разные свойства с разными значениями.

Теперь у нас есть полностью функционирующая игра «Угадай число», и теперь мы сделаем ее более похожей на полноценный Numble.

Четыре предположения

Наша первая задача состоит в том, чтобы позволить пользователю 4 предположения. В Wordle разрешено 6 догадок для слова из 5 букв, поэтому для Numble мы допустим 4 догадки для трехзначного числа.

Для этого нам придется удалить guess свойство и добавить к объекту еще два свойства State:

const State = {

started: false,

digits: Array (10).fill («grey»),

guesses: Array (4).fill (Array (3).fill (null)),

guessCount: 0,

count: 0,

start, finish, check, appear, remove,

View

}

Как видите, теперь у нас есть guesses свойство вместо того, guess что было раньше. Значение guesses представляет собой 2D-массив из 4 массивов, каждый из которых имеет длину три и заполнен null. Если вы не знакомы с этой Array.fill () функцией, это быстрый способ создания массива, что означает, что нам не нужно записывать массив полностью.

Каждый из 4 вложенных массивов представляет одно из 4 предположений, которые сделает пользователь. Например, если первое предположение было 123, guesses массив будет выглядеть так:

[[1,2,3], [null, null, null], [null, null, null], [null, null, null]]

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

Кроме того, у нас есть guessCount свойство, для которого установлено значение 0. Хотя оно похоже на count свойство, оно позволит нам отслеживать количество предположений, сделанных пользователем.

Эта диаграмма должна помочь вам визуализировать и полностью понять необходимость свойств count и: guessCount

Аннотированная Numble Grid

Как видите, guessCount это индекс вложенного массива, в котором хранится догадка, и count индекс каждой отдельной цифры каждой догадки.

Теперь нам нужно внести некоторые изменения в View функцию:

const View = state => html`

Numble

${state.started?

html`

${state.guesses.map ( (guess, i) => html`

${guess.map ( (number, j)=> html`
${number}
`) }
`) }

${state.feedback}

${state.digits.map ( (digit, index) => html`<button onclick=${state.appear (index) }>${index}`) }

<button onclick=${state.remove}>DELETE

<button onclick=${state.check}>ENTER

<button onclick=${state.finish}>END`

html`<button onclick=${state.start}>START`

}`

Это почти идентично тому, что View мы создали ранее, однако div с идентификатором «догадки» изменился. Дело в том, что теперь мы используем 2D-массив для отображения 4 догадок в виде сетки, нам понадобится вложенная карта.

Совет по кодированию: при использовании вложенной карты для индекса каждой карты мы будем использовать i для первой карты и j для второй. Вы можете использовать то, что считаете наиболее простым для вас, если они не совпадают!

Первая карта повторяет каждое предположение как строку сетки. Затем вторая карта перебирает каждую отдельную цифру этого предположения и отображает соответствующий HTML, чтобы показать угаданную цифру или пустой кружок. При этом ваш экран должен выглядеть так:

Намбл Сетка

Этот новый макет означает, что мы также должны изменить функции appear и remove. Это относительно просто, но опять же требует двойной карты.

const appear = guess => event => {

Update (state => ({

guesses: state.guesses.map ( (array, i) => i === state.guessCount? array.map ( (digit, j) => j === state.count? guess: digit): array),

count: state.count + 1

}))

}

Мы обновляем guesses свойство здесь, и именно здесь два разных count свойства станут действительно полезными.

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

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

Как и раньше, remove функция работает практически идентично.

const remove = event => {

Update (state => ({

guesses: state.guesses.map ( (array, i) => i === state.guessCount? array.map ( (digit, j)=> j === state.count — 1? null: digit): array),

count: state.count — 1

}))

}

Первая карта здесь снова просто определяет, какое предположение делает пользователь, а вторая следует той же логике, что и наша исходная remove функция.

Однако count свойство уменьшается, чтобы пользователь мог сделать предположение повторно.

Наконец, нам нужно внести некоторые изменения в check функцию. Это функция, которая запускается каждый раз, когда пользователь отправляет предположение.

const check = event => {

Update (state => {

const numble = state.guesses[state.guessCount].join («») === state.number

return {

feedback: numble? «NUMBLE!»: state.guessCount < 3? «Keep going...»: `Nope! It was ${state.number}`,

guessCount: state.guessCount + 1,

count: 0

}

})

}

Здесь изменились только две вещи, и обе находятся в возвращаемом объекте. Свойство feedback имеет дополнительную логику, чтобы сделать приложение немного более динамичным. В отзыве теперь будет отображаться сообщение, чтобы пользователь знал, как у него дела.

В этом случае мы имеем: если numble есть, true другими словами, если догадка пользователя верна, обратная связь становится «NUMBLE»; если numble есть false, проверьте, меньше ли догадка трех (по сути, это проверяет, сделал ли пользователь окончательное предположение). Если да, то обратная связь «Продолжайте...», иначе «Нет! Это был (ответ)».

И это все для первой части! Вы можете увидеть полный код в codepen ниже:

Научитесь программировать с помощью JavaScript — бесплатно

Логика цвета

Как отмечалось в самом начале статьи, цвета являются основным направлением Wordle и, следовательно, Numble. Если вы еще не играли в Numble или Wordle, мы настоятельно рекомендуем вам это сделать, чтобы правильно понять, как работают цвета.

Это пример системы окраски, используемой Numble:

Пример цвета Numble

После того, как пользователь делает предположение, цвета обновляются в двух местах: в самом предположении и на клавиатуре. Логика для них совершенно одинакова, поэтому мы можем создать стрелочную функцию с именем getColors, которая принимает предположение и фактическое число в качестве параметров.

const getColors = (guess, number) => guess.map ( (digit, index) => number.includes (digit)? digit.toString () === number[index]? «green»: «yellow»: «black»)

Мы сопоставляем массив «догадка» и с помощью метода «String.includes (item)» сначала проверяем, включает ли ответ цифру предположения. Если это так, то мы проверяем, находится ли цифра на правильном месте. Если да, то цвет назначается «зеленый». Если нет, то цвет «желтый». В противном случае цифры вообще нет в ответе, и поэтому цвет «черный».

Затем эта стрелочная функция должна возвращать массив с тремя элементами: «зелеными», «желтыми» или «черными», соответствующими каждой цифре в «догадке».

Например, если мы вызвали функцию с помощью getColors ([1,2,3], «327»), то массив, который мы должны вернуть, будет [«black», «green», «yellow«]

Вы могли заметить, что нам пришлось заменить цифру строкой. Это потому, что нам нужно сравнить его с ответом, который хранится в виде строки, и вы не можете сравнить два элемента, если они имеют разные типы. Что ж, вы можете попробовать, но будьте готовы войти в целый мир боли приведения типов JavaScript.

Примечание. Wordle работает с дубликатами по-другому, поэтому, если вы хотите немного усложнить задачу, вы можете попробовать имитировать метод Wordle.

В следующей части нам не нужно вносить какие-либо изменения или добавлять что-либо в State, но View это становится немного сложнее. Как кратко упоминалось в первой статье, мы собираемся использовать классы CSS, чтобы позволить нам изменять цвета.

const View = state => html`

Numble

${state.started?

html`

 

${state.guesses.map ( (guess, i) => html`

${number}
`) }
`) }

${state.feedback}

${state.digits.map ( (digit, index) => html`<button class=${digit} onclick=${state.appear (index) }>${index}`) }

<button onclick=${state.remove}>DELETE

<button onclick=${state.check}>ENTER

<button onclick=${state.finish}>END`

html`<button onclick=${state.start}>START`

}`

Как видите, единственные две вещи, которые изменились, — это классы CSS для кнопок клавиатуры и отдельных разделов каждой строки.

Начиная с div «догадки», у нас есть следующая логика:

state.guessCount > i? getColors (guess, state.number)[j]: «grey»

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

Вот как должен выглядеть ваш экран после того, как пользователь сделал одно предположение:

Первое предположение

Массив из getColors функции:

[«yellow», «black», «black»]

Таким образом, пользователь теперь будет знать, что 3 есть в числе, но не в том месте, а 4 и 5 вообще не в числе.

Логика клавиатуры намного проще, но она по-прежнему использует ту же getColor функцию, которую мы написали ранее. Помните, как мы заполняли digits массив «серым» цветом? Вот почему мы это сделали.

Поскольку клавиатура отображается на экране, класс представляет собой просто значение по индексу клавиши в digits массиве. Позже мы рассмотрим, как мы можем изменить цвет, но, используя приведенный выше пример, после первого предположения digits массив должен выглядеть так:

[«grey», «grey», «grey», «yellow», «black», «black», «grey», «grey», «grey», «grey»]

Мы почти у цели! Наша последняя задача — изменить check функцию.

const check = event => {

Update (state => {

const guess = state.guesses[state.guessCount]

const numble = guess.join`` === state.number

const colors = getColors (guess, state.number)

return {

feedback: numble? «NUMBLE!»: state.guessCount < 3? «Keep going...»: `Nope! It was ${state.number}`,

digits: state.digits.map ( (colour, digit) => guess.includes (digit)? colors[guess.indexOf (digit) ]: colour),

guessCount: state.guessCount + 1,

count: 0

}

})

}

В Update функции есть еще две константы. Это просто упрощает логику в возвращаемом объекте.

У нас guess есть массив из трех цифр, которые пользователь только что угадал (отсюда и использование state.guessCount). У нас есть то же, что и numble раньше, но на этот раз с использованием guess только что созданной константы. Это просто помогает иметь более чистый код и избегать повторений. Наконец, у нас colors есть массив, возвращаемый при getColors запуске функции с текущим предположением пользователя и ответом.

Это обновит массив цифр и обеспечит правильное окрашивание цифр на клавиатуре после каждого предположения.

Теперь возвращаемый объект идентичен приведенному выше, но мы также обновляем digits свойство.

state.digits.map ( (color, digit) => guess.includes (digit)? colors[guess.indexOf (digit) ]: color)

Это наша последняя функция отображения! И, по сути, проверяет, соответствует ли число на клавиатуре (которое является digit). Если это текущий цвет, он должен быть заменен цветом, сгенерированным getColors функцией, в противном случае цвет должен остаться прежним.

Используя то же предположение, что и выше, мы можем увидеть, как должна выглядеть клавиатура:

Клавиатура первого предположения

Вот и все! Полнофункциональная версия Numble!

Опять же, вот как должен выглядеть код целиком:

В актуальную версию Numble я добавил ряд функций, чтобы сделать игру еще более динамичной. Если вы хотите бросить вызов себе и добавить некоторые дополнительные функции, вот несколько советов из моей финальной версии Numble:

Сыграйте снова — позвольте пользователю играть столько раз, сколько он хочет, или сделайте так, чтобы в день было только одно испытание.

Streak — отслеживает количество правильных ответов подряд.

Лучшая серия — самая длинная серия, которую сохранил пользователь.

Темный режим — скорее вызов CSS, но, тем не менее, интересный

Показать статистику — разбивка того, сколько догадок потребовалось пользователю для каждой игры.

Функция «Поделиться» — позволяет пользователям делиться своими лучшими результатами.

Я очень надеюсь, что вы получили такое же удовольствие от создания Numble, как и я!

Делитесь нашими материалами с друзьями!

 

 

Заказать разработку сайта