Делаем игру Wordle на JavaScript

Делаем игру Wordle на JavaScript

Прокачиваем свой английский

Wordle — когда-то популярная игра в сети. Правила такие: есть поле 5 на 6 клеток и загаданное слово, а у игрока есть 6 попыток угадать это слово. При каждой попытке он пишет свой вариант на новой строке поля, а игра подсвечивает буквы:

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

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

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

В проекте используется AJAX — технология обновления данных на странице без её перезагрузки. Чтобы запустить проект дома, установите LAMP, MAMP или любой другой веб-сервер. Если есть желание, можно сразу отправить всё на свой хостинг — в этом случае тоже всё будет работать.

Особенность проекта

Сегодня мы попробуем несколько новых штук, которые не использовали до этого:

  • подключение модулей и использование переменных оттуда;
  • работа с уведомлениями на странице.

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

Создаём страницу

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

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wordle</title>
</head>
<body>
    <h1> Играем в Wordle </h1>
    
    <div id="game-board">

    </div>
</body>
</html>

Чтобы игра выглядела красиво, используем стили Toastr для внутриигровых уведомлений и сразу подключим наш пока пустой файл со стилями style.css. Добавим всё это в раздел <head>, а про сами уведомления поговорим чуть позже:

<link rel="stylesheet" href="style.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet"/>

Последнее, что нам нужно на этом этапе — подключить наш основной скрипт, который возьмёт на себя всю работу, скрипт уведомлений Toastr (именно в нём используется AJAX) и jQuery для управления страницей изнутри. Добавим все скрипты в самый конец страницы перед закрывающим тегом </body>:

<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="script.js" type="module"></script>

Если открыть эту страницу в браузере, то кроме заголовка мы пока ничего не увидим — у нас ещё нет скрипта, который бы сделал всё остальное. Но раз мы уже видим заголовок, добавим в файл со стилями запись о том, что его нужно выровнять по центру:

/* заголовок страницы */
h1 {
text-align: center;
}

Делаем игру Wordle на JavaScript

Создаём файл со словами

Для игры нам понадобятся все английские слова из 5 букв. Если верить словарю, их всего 5756, и переменная такого размера будет выглядеть катастрофически большой. Чтобы не раздувать наш основной скрипт, будем хранить все слова как элементы массива в отдельном модуле words.js, а в основном скрипте просто импортируем эту переменную из файла.

Мы уже собрали для вас этот файлик, можете поглядеть: mihailmaximov.ru

Пишем скрипт

В самом начале скрипта опишем все глобальные переменные, которые нам понадобятся, причём слова импортируем из файла командой import{}. Загадываем слово так: берём случайное число от 0 до 1, умножаем на количество слов и округляем результат до целого. На всякий случай выведем в консоль загаданное слово — так мы сможем проверить, правильно ли работает наша программа.

// импортируем слова из файла
import { WORDS } from "./words.js";

// количество попыток
const NUMBER_OF_GUESSES = 6;
// сколько попыток осталось
let guessesRemaining = NUMBER_OF_GUESSES;
// текущая попытка
let currentGuess = [];
// следующая буква
let nextLetter = 0;
// загаданное слово
let rightGuessString = WORDS[Math.floor(Math.random() * WORDS.length)]
// на всякий случай выведем в консоль загаданное слово, чтобы проверить, как работает игра
console.log(rightGuessString)

Теперь создадим игровую доску — нам нужно сделать её 5 на 6 клеток и предусмотреть разбиение на строки. Дело в том, что для проверки каждого нового слова нам понадобится содержимое всей строки, а это удобнее делать, когда к ней можно обратиться напрямую.

// создаём игровое поле
function initBoard() {
    // получаем доступ к блоку на странице
    let board = document.getElementById("game-board");

    // создаём строки
    // делаем цикл от 1 до 6, потому что попыток у нас как раз 6
    for (let i = 0; i < NUMBER_OF_GUESSES; i++) {
        // создаём новый блок на странице
        let row = document.createElement("div")
        // добавляем к нему класс, чтобы потом работать со строками напрямую
        row.className = "letter-row"
        
        // создаём отдельные клетки
        // добавляем по 5 клеток в ряд
        for (let j = 0; j < 5; j++) {
            // создаём новый блок на странице
            let box = document.createElement("div")
            // добавляем к нему класс
            box.className = "letter-box"
            // вкладываем новый блок внутрь блока со строкой
            row.appendChild(box)
        }

        // как все 5 клеток готовы, добавляем новую строку на поле
        board.appendChild(row)
    }
}

// рисуем игровое поле
initBoard();

Сразу же добавим оформление в файл со стилями — нам нужны стили для всей доски целиком, для строки и для отдельной клетки:

/* стиль для всей доски */
#game-board {
  /* делаем выравнивание всех элементов по центру */
  display: flex;
  align-items: center;
  /* добавляем выравнивание по вертикали */
  flex-direction: column;
}

/* стиль для строки */
.letter-row {
  /* каждая клетка пусть будет в своём отдельном контейнере */
  display: flex;
}

/* стиль для клетки */
.letter-box {
  /* рисуем границу */
  border: 2px solid gray;
  border-radius: 3px;
  /* добавляем отступы */
  margin: 2px;
  /* размер шрифта */
  font-size: 2.5rem;
  font-weight: 700;
  /* высота и ширина клетки */
  height: 3rem;
  width: 3rem;
  /* выравниваем содержимое по центру */
  display: flex;
  justify-content: center;
  align-items: center;
  /* делаем все буквы большими */
  text-transform: uppercase;
}
Делаем игру Wordle на JavaScript

Вводим слова

Чтобы игрок мог вводить слова, добавим обработчик события "keydown" — оно сработает в момент нажатия клавиши. Работать алгоритм будет так:

  1. Если все попытки угадать слово истрачены, обработчик тут же завершается, потому что вводить новые символы просто некуда.
  2. Если нажата клавиша Backspace — вызывается функция удаления последнего символа.
  3. Если нажат Enter — вызывается функция проверки слова, угадали мы или нет.
  4. А если нажата какая-то другая клавиша, то алгоритм проверит, попадают ли введённые символы в английский алфавит (потому что у нас сейчас версия для английского языка). Если попадают — вызывается функция добавления символа в клетку. Здесь нам пригодятся регулярные выражения, с которыми мы уже работали в других проектах.

Запишем это на JavaScript:

// обработчик нажатия на клавиши
document.addEventListener("keydown", (e) => {

    // если попыток не осталось
    if (guessesRemaining === 0) {
        // выходим из функции
        return
    }

    // получаем код нажатой клавиши
    let pressedKey = String(e.key)
    // если нажат Backspace и в строке есть хоть один символ
    if (pressedKey === "Backspace" && nextLetter !== 0) {
        // то удаляем последнюю введённую букву
        deleteLetter();
        // и выходим из обработчика
        return;
    }

    // если нажат Enter
    if (pressedKey === "Enter") {
        // проверяем введённое слово
        checkGuess();
        // и выходим из обработчика
        return;
    }

    // проверяем, есть ли введённый символ в английском алфавите
    let found = pressedKey.match(/[a-z]/gi)
    // если нет
    if (!found || found.length > 1) {
        // то выходим из обработчика
        return
    // иначе добавляем введённую букву в новую клетку
    } else {
        insertLetter(pressedKey)
    }
})

Если обновить страницу с проектом в браузере, то при попытке ввода получим ошибку — всё потому, что у нас нет ни одной функции, на которые мы здесь ссылаемся. Исправим это и добавим их все по очереди.

Добавляем вывод букв на экран

Нам нужно написать функцию insertLetter(), которая будет рисовать в клетках вводимые буквы. Смысл в том, что нам сначала надо проверить — а есть ли вообще свободные клетки в строке. Если есть — получаем номер клетки, добавляем в неё текст и переходим к следующей. 

Чтобы было красивее, нарисуем жирную обводку везде, где есть буквы. Для этого добавим класс "filled-box" к текущему элементу и не забудем прописать его в стилях.

// выводим букву в клетку
function insertLetter (pressedKey) {
    // если клетки закончились
    if (nextLetter === 5) {
        // выходим из функции
        return;
    }
    // получаем доступ к текущей строке
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    // и к текущей клетке, где будет появляться буква
    let box = row.children[nextLetter]
    // меняем текст в блоке с клеткой на нажатый символ
    box.textContent = pressedKey
    // добавляем к клетке жирную обводку
    box.classList.add("filled-box")
    // добавляем введённый символ к массиву, в которой хранится наша текущая попытка угадать слово
    currentGuess.push(pressedKey)
    // помечаем, что дальше будем работать со следующей клеткой
    nextLetter += 1
}

И добавляем короткий класс "filled-box" в файл со стилями:

/* добавляем жирную обводку у заполненных клеток */
.filled-box {
  border: 3px solid black;
}
Делаем игру Wordle на JavaScript
Теперь мы можем вводить слова, но исправить пока ничего нельзя

Удаляем символы

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

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

// удаление символа
function deleteLetter () {
    // получаем доступ к текущей строке
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    // и к последнему введённому символу
    let box = row.children[nextLetter - 1]
    // очищаем содержимое клетки
    box.textContent = ""
    // убираем жирную обводку
    box.classList.remove("filled-box")
    // удаляем последний символ из массива с нашей текущей догадкой
    currentGuess.pop()
    // помечаем, что у нас теперь на одну свободную клетку больше
    nextLetter -= 1
}

Проверяем догадку

Когда мы ввели все буквы в строке, то последнее, что нам осталось сделать по нажатию Enter, — проверить нашу догадку и подсветить буквы разными цветами.

Для этого мы сначала собираем строку с догадкой из символов в массиве, а потом начинаем проверять. Если букв меньше 5 — выводим уведомление, причём у нас сразу получится красивое уведомление, потому что будем использовать Toastr. В этой библиотеке уже есть стандартные уведомления об ошибках, поэтому нам достаточно использовать команду toastr.error("Введены не все буквы!"), чтобы сразу получить стильное уведомление в правом верхнем углу экрана.

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

Если в догадке 5 букв и слово есть в списке, то начинаем раскрашивать клетки в зависимости от того, угадали мы с позицией буквы или нет. Когда все буквы станут зелёными, то сразу обнуляем количество попыток и с помощью Toastr выводим уведомление о победе. А если все попытки закончились — сообщение о том, что мы проиграли.

// проверка введённого слова
function checkGuess () {
    // получаем доступ к текущей строке
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    // переменная, где будет наша догадка
    let guessString = ''
    // делаем из загаданного слова массив символов
    let rightGuess = Array.from(rightGuessString)

    // собираем все введённые в строке буквы в одно слово
    for (const val of currentGuess) {
        guessString += val
    }

    // если в догадке меньше 5 букв — выводим уведомление, что букв не хватает
    if (guessString.length != 5) {
        // error означает, что уведомление будет в формате ошибки
        toastr.error("Введены не все буквы!");
        // и после вывода выходим из проверки догадки
        return;
    }

    // если введённого слова нет в списке возможных слов — выводим уведомление
    if (!WORDS.includes(guessString)) {
        toastr.error("Такого слова нет в списке!")
        // и после вывода выходим из проверки догадки
        return;
    }

    // перебираем все буквы в строке, чтобы подсветить их нужным цветом
    for (let i = 0; i < 5; i++) {
        // убираем текущий цвет, если он был
        let letterColor = ''
        // получаем доступ к текущей клетке
        let box = row.children[i]
        // и к текущей букве в догадке
        let letter = currentGuess[i]
        
        // смотрим, на каком месте в исходном слове стоит текущая буква
        let letterPosition = rightGuess.indexOf(currentGuess[i])
        // если такой буквы нет в исходном слове
        if (letterPosition === -1) {
            // закрашиваем клетку серым
            letterColor = 'grey'
        // иначе, когда мы точно знаем, что буква в слове есть
        } else {
            // если позиция в слове совпадает с текущей позицией 
            if (currentGuess[i] === rightGuess[i]) {
                // закрашиваем клетку зелёным
                letterColor = 'green'
            } else {
                // в противном случае закрашиваем жёлтым
                letterColor = 'yellow'
            }

            // заменяем обработанный символ на знак решётки, чтобы не использовать его на следующем шаге цикла
            rightGuess[letterPosition] = "#"
        }

        // применяем выбранный цвет к фону клетки
        box.style.backgroundColor = letterColor;
    }

    // если мы угадали
    if (guessString === rightGuessString) {
        // выводим сообщение об успехе
        toastr.success("Вы выиграли!")
        // обнуляем количество попыток
        guessesRemaining = 0;
        // выходим из проверки
        return
    // в противном случае
    } else {
        // уменьшаем счётчик попыток
        guessesRemaining -= 1;
        // обнуляем массив с символами текущей попытки
        currentGuess = [];
        // начинаем отсчёт букв заново
        nextLetter = 0;

        // если попытки закончились
        if (guessesRemaining === 0) {
            // выводим сообщение о проигрыше
            toastr.error("У вас не осталось попыток. Вы проиграли!");
            // и выводим загаданное слово
            toastr.info(`Загаданное слово: "${rightGuessString}"`)
        }
    }
}
Делаем игру Wordle на JavaScript

Поиграть на странице проекта

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wordle</title>
    <link rel="stylesheet" href="style.css">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet"/>
</head>
<body>
    <h1> Играем в Wordle </h1>
    
    <div id="game-board">

    </div>

<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="script.js" type="module"></script>
</body>
</html>

/* заголовок страницы */
h1 {
 text-align: center;
}

/* стиль для всей доски */
#game-board {
  /* делаем выравнивание всех элементов по центру */
  display: flex;
  align-items: center;
  /* добавляем выравнивание по вертикали */
  flex-direction: column;
}

/* стиль для строки */
.letter-row {
  /* каждая клетка пусть будет в своём отдельном контейнере */
  display: flex;
}

/* стиль для клетки */
.letter-box {
  /* рисуем границу */
  border: 2px solid gray;
  border-radius: 3px;
  /* добавляем отступы */
  margin: 2px;
  /* размер шрифта */
  font-size: 2.5rem;
  font-weight: 700;
  /* высота и ширина клетки */
  height: 3rem;
  width: 3rem;
  /* выравниваем содержимое по центру */
  display: flex;
  justify-content: center;
  align-items: center;
  /* делаем все буквы большими */
  text-transform: uppercase;
}

/* добавляем жирную обводку у заполненных клеток */
.filled-box {
  border: 3px solid black;
}

// импортируем слова из файла
import { WORDS } from "./words.js";

// количество попыток
const NUMBER_OF_GUESSES = 6;
// сколько попыток осталось
let guessesRemaining = NUMBER_OF_GUESSES;
// текущая попытка
let currentGuess = [];
// следующая буква
let nextLetter = 0;
// загаданное слово
let rightGuessString = WORDS[Math.floor(Math.random() * WORDS.length)]
// на всякий случай выведем в консоль загаданное слово, чтобы проверить, как работает игра
console.log(rightGuessString)

// создаём игровое поле
function initBoard() {
    // получаем доступ к блоку на странице
    let board = document.getElementById("game-board");

    // создаём строки
    // делаем цикл от 1 до 6, потому что попыток у нас как раз 6
    for (let i = 0; i < NUMBER_OF_GUESSES; i++) {
        // создаём новый блок на странице
        let row = document.createElement("div")
        // добавляем к нему класс, чтобы потом работать со строками напрямую
        row.className = "letter-row"
        
        // создаём отдельные клетки
        // добавляем по 5 клеток в ряд
        for (let j = 0; j < 5; j++) {
            // создаём новый блок на странице
            let box = document.createElement("div")
            // добавляем к нему класс
            box.className = "letter-box"
            // вкладываем новый блок внутрь блока со строкой
            row.appendChild(box)
        }

        // как все 5 клеток готовы, добавляем новую строку на поле
        board.appendChild(row)
    }
}

// удаление символа
function deleteLetter () {
    // получаем доступ к текущей строке
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    // и к последнему введённому символу
    let box = row.children[nextLetter - 1]
    // очищаем содержимое клетки
    box.textContent = ""
    // убираем жирную обводку
    box.classList.remove("filled-box")
    // удаляем последний символ из массива с нашей текущей догадкой
    currentGuess.pop()
    // помечаем, что у нас теперь на одну свободную клетку больше
    nextLetter -= 1
}

// проверка введённого слова
function checkGuess () {
    // получаем доступ к текущей строке
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    // переменная, где будет наша догадка
    let guessString = ''
    // делаем из загаданного слова массив символов
    let rightGuess = Array.from(rightGuessString)

    // собираем все введённые в строке буквы в одно слово
    for (const val of currentGuess) {
        guessString += val
    }

    // если в догадке меньше 5 букв — выводим уведомление, что букв не хватает
    if (guessString.length != 5) {
        // error означает, что уведомление будет в формате ошибки
        toastr.error("Введены не все буквы!");
        // и после вывода выходим из проверки догадки
        return;
    }

    // если введённого слова нет в списке возможных слов — выводим уведомление
    if (!WORDS.includes(guessString)) {
        toastr.error("Такого слова нет в списке!")
        // и после вывода выходим из проверки догадки
        return;
    }

    // перебираем все буквы в строке, чтобы подсветить их нужным цветом
    for (let i = 0; i < 5; i++) {
        // убираем текущий цвет, если он был
        let letterColor = ''
        // получаем доступ к текущей клетке
        let box = row.children[i]
        // и к текущей букве в догадке
        let letter = currentGuess[i]
        
        // смотрим, на каком месте в исходном слове стоит текущая буква
        let letterPosition = rightGuess.indexOf(currentGuess[i])
        // если такой буквы нет в исходном слове
        if (letterPosition === -1) {
            // закрашиваем клетку серым
            letterColor = 'grey'
        // иначе, когда мы точно знаем, что буква в слове есть
        } else {
            // если позиция в слове совпадает с текущей позицией 
            if (currentGuess[i] === rightGuess[i]) {
                // закрашиваем клетку зелёным
                letterColor = 'green'
            } else {
                // в противном случае закрашиваем жёлтым
                letterColor = 'yellow'
            }

            // заменяем обработанный символ на знак решётки, чтобы не использовать его на следующем шаге цикла
            rightGuess[letterPosition] = "#"
        }

        // применяем выбранный цвет к фону клетки
        box.style.backgroundColor = letterColor;
    }

    // если мы угадали
    if (guessString === rightGuessString) {
        // выводим сообщение об успехе
        toastr.success("Вы выиграли!")
        // обнуляем количество попыток
        guessesRemaining = 0;
        // выходим из проверки
        return
    // в противном случае
    } else {
        // уменьшаем счётчик попыток
        guessesRemaining -= 1;
        // обнуляем массив с символами текущей попытки
        currentGuess = [];
        // начинаем отсчёт букв заново
        nextLetter = 0;

        // если попытки закончились
        if (guessesRemaining === 0) {
            // выводим сообщение о проигрыше
            toastr.error("У вас не осталось попыток. Вы проиграли!");
            // и выводим загаданное слово
            toastr.info(`Загаданное слово: "${rightGuessString}"`)
        }
    }
}

// выводим букву в клетку
function insertLetter (pressedKey) {
    // если клетки закончились
    if (nextLetter === 5) {
        // выходим из функции
        return;
    }
    // получаем доступ к текущей строке
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    // и к текущей клетке, где будет появляться буква
    let box = row.children[nextLetter]
    // меняем текст в блоке с клеткой на нажатый символ
    box.textContent = pressedKey
    // добавляем к клетке жирную обводку
    box.classList.add("filled-box")
    // добавляем введённый символ к массиву, в которой хранится наша текущая попытка угадать слово
    currentGuess.push(pressedKey)
    // помечаем, что дальше будем работать со следующей клеткой
    nextLetter += 1
}

// обработчик нажатия на клавиши
document.addEventListener("keydown", (e) => {

    // если попыток не осталось
    if (guessesRemaining === 0) {
        // выходим из функции
        return
    }

    // получаем код нажатой клавиши
    let pressedKey = String(e.key)
    // если нажат Backspace и в строке есть хоть один символ
    if (pressedKey === "Backspace" && nextLetter !== 0) {
        // то удаляем последнюю введённую букву
        deleteLetter();
        // и выходим из обработчика
        return;
    }

    // если нажат Enter
    if (pressedKey === "Enter") {
        // проверяем введённое слово
        checkGuess();
        // и выходим из обработчика
        return;
    }

    // проверяем, есть ли введённый символ в английском алфавите
    let found = pressedKey.match(/[a-z]/gi)
    // если нет
    if (!found || found.length > 1) {
        // то выходим из обработчика
        return
    // иначе добавляем введённую букву в новую клетку
    } else {
        insertLetter(pressedKey)
    }
})


initBoard();

Что дальше

Сейчас уже можно полноценно играть и угадывать слова, но можно сделать следующий шаг:

  • добавить наэкранную клавиатуру, чтобы можно было играть на устройствах без клавиатуры;
  • добавить красивую анимацию, чтобы игра выглядела эффектнее;
  • сделать русскую версию.

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

Исходный код:

Пол Акиньеми

Текст:

Михаил Полянин

Редактор:

Максим Ильяхов

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Виталий Вебер

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
Объясни мне: как опубликовать свой сайт в интернете

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

medium
SSL-сертификаты и безопасный интернет

Ваше соединение надёжно защищено. Или нет.

easy
Лучшие языки программирования для старта в 2020 году

Что выбрать, если хочешь стать программистом в этом году.

easy
Все опять заговорили про VPN. Что это такое, объясни мне!

Мы не можем вам сказать. Но можем показать.

medium
Что такое права доступа и на что они влияют
Что такое права доступа

И на что они влияют

medium
Как это работает: вход на сайты через соцсети
Как это работает: вход на сайты через соцсети

Всё дело в единой авторизации.

easy
Самые странные языки программирования
Самые странные языки программирования

Как вообще можно было такое придумать?

easy
Экзотические виды памяти
Экзотические виды памяти

Как насчёт памяти на барабанах или на проволоке?

medium
Markdown: что это и кому нужно
Markdown: что это и кому нужно

Для всех, кто пишет контент, сайты и программы.

easy
Как в России появился каршеринг
Как в России появился каршеринг

Конспект подкаста «Запуск завтра»

easy
medium