Русский
Русский
English
Статистика
Реклама

2048 WASM или игра на Rust за 2 дня

Данная статья представляет из-себя эксперимент с Rust'ом с последующей его компиляцией в WASM. Было интересно пощупать данные технологии на чем-то сложнее, чем вычисление факториала, поэтому выбор пал на всем известную игру 2048.




Описание игры


Оригинальная игра представляет из себя клеточное поле 4 на 4. Каждая клетка может быть либо пустой, либо занята плиткой
с числом, являющегося степенью двойки. Стартовое состояние поля имеет 2 заполненные клетки.




Игрок может совершать ход в одном из 4 направлений: влево, вправо, вверх и вниз, что сдвигает все плитки до упора в выбранном
направлении. После каждого хода появляется новая плитка. Новая плитка будет содержать двойку с вероятностью 90% или четверку
с вероятностью 10%.


Походим из стартового состояния, показанного выше, вправо:




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


Если плитка упирается в другую плитку, содержащее такое же число, они соединяются в одну плитку следующей степени двойки.


Походим из предыдущего состояния вверх:




Двойки последнего столбца первой и второй строк объединилсь в одну, а кроме этого на третьем ряду появилась четверка.


Цель игры: дойти до плитки с числом 2048.


Подготовка


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


На данной странице представлен список фронтовых фреймворков для Rust.
Самый популярный вариант Yew выглядит интересно, особенно при наличии опыта с React'ом, документация и примеры присутствуют,
так что выбор пал на него.


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


cargo new --lib rust-2048 && cd rust-2048

Дальше, в Cargo.toml прописываем зависимости:


[dependencies]yew = "0.17"wasm-bindgen = "0.2.67"

В примере предоставлен код для создания простого счетчика, который нужно поместить в src/lib.rs:


use wasm_bindgen::prelude::*;use yew::prelude::*;struct Model {    link: ComponentLink<Self>,    value: i64,}enum Msg {    AddOne,}impl Component for Model {    type Message = Msg;    type Properties = ();    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {        Self {            link,            value: 0,        }    }    fn update(&mut self, msg: Self::Message) -> ShouldRender {        match msg {            Msg::AddOne => self.value += 1        }        true    }    fn change(&mut self, _props: Self::Properties) -> ShouldRender {        // Should only return "true" if new properties are different to        // previously received properties.        // This component has no properties so we will always return "false".        false    }    fn view(&self) -> Html {        html! {            <div>                <button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>                <p>{ self.value }</p>            </div>        }    }}#[wasm_bindgen(start)]pub fn run_app() {    App::<Model>::new().mount_to_body();}

Создаем директорию для статичных файлов:


mkdir static

Внутри которой создаем index.html со следующим содержимым:


<!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8" />    <title>Yew Sample App</title>    <script type="module">      import init from "./wasm.js";      init();    </script>  </head>  <body></body></html>

Собираем проект при помощи wasm-pack (устанавливается через cargo install wasm-pack):


wasm-pack build --target web --out-name wasm --out-dir ./static

У меня при сборке появилась следующая ошибка:


Error: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:[lib]crate-type = ["cdylib", "rlib"]

Окей, так и сделаем. Открываем Cargo.toml и прописываем в конце:


[lib]crate-type = ["cdylib", "rlib"]

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


cd staticpython -m SimpleHTTPServer

Открываем http://127.0.0.1:8000 и видим рабочий счетчик. Отлично, теперь можно приступать к самой игре.


Основа


Коммит: https://github.com/dev-family/wasm-2048/commit/6bc015fbc88c1633f4605944fd920b2780e261c1


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


Рассмотрим логику движения плиток на примере данного поля:


2, 4, 0, 00, 0, 0, 00, 0, 0, 00, 0, 0, 0

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


0, 0, 2, 40, 0, 0, 00, 0, 0, 00, 0, 0, 0

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


  1. Выбрано направление вправо => идем справа влево.

         /-------- пустая клетка, пропускаем2, 4, 0, 00, 0, 0, 00, 0, 0, 00, 0, 0, 0

2.


      /-------- пустая клетка, пропускаем2, 4, 0, 00, 0, 0, 00, 0, 0, 00, 0, 0, 0

3.


    /-------- начинаем сдвигать вправо2, 4, 0, 00, 0, 0, 00, 0, 0, 00, 0, 0, 0

4.


       /-------- продолжаем2, 0, 4, 00, 0, 0, 00, 0, 0, 00, 0, 0, 0

5.


         /-------- дошли до конца, возвращаемся на следующую позицию от которой начали передвижение2, 0, 0, 40, 0, 0, 00, 0, 0, 00, 0, 0, 0

6.


 /-------- двигаем вправо2, 0, 0, 40, 0, 0, 00, 0, 0, 00, 0, 0, 0

7.


    /-------- продолжаем0, 2, 0, 40, 0, 0, 00, 0, 0, 00, 0, 0, 0

8.


       /-------- остонавливаемся и переходим к следующей строке0, 0, 2, 40, 0, 0, 00, 0, 0, 00, 0, 0, 0

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


В коде данный алгоритм реализован методами Direction#build_traversal (построение пути обхода поля),
Grid#traverse_from (продвижение конкретной клетки по направлению) и Grid#move_in публичный метод, использующий предыдущие два.


Объединения


Коммит: https://github.com/dev-family/wasm-2048/commit/7e08b5af6008e6f9d716d7c9a41b0810e869df9e


У объединения плиток есть одна особенность: плитки не объединяются больше одного раза,
т.е. в случае наезжающих друг на друга 4 одинаковых плит, результатом будет являться 2 плитки
следующей степени. Из этого следует, что у плиток должно появиться некое состояние, которое нужно
сбрасывать перед каждым ходом.


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


Добавление новых плиток


Коммит: https://github.com/dev-family/wasm-2048/commit/6082409412f6c19943c453c5d706d57bbcef538b


Для рандома используется пакет rand, который работает и в WASM среде
путем добавления wasm-bindgen feature.


Для того, чтобы не ломать предыдущие тесты, которые не рассчитаны на добавление новых случайных
плиток в структуру Grid было добавлено новое флаговое поле enable_new_tiles.


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


UI


Коммит: https://github.com/dev-family/wasm-2048/commit/356e0889a84d7fc2582662e76238f94fc69bfed7


UI часть реализуется довольно просто, особенно для знакомых с React и/или JSX. Прочитать про html! макрос из Yew можно здесь,
а про компоненты в общем здесь. Мне очень напомнило React в пре-хуковых времен.


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


Анимации


Коммит: https://github.com/dev-family/wasm-2048/commit/e258748ab114ec5f930dbeb77d674bdbc5e63b1a.


Чтобы интерфейс смотрелся более живым, необходимо добавить анимации.


Они реализованы поверх обычных CSS transitions. Мы учим плитки запоминать их предыдущую позицию и при рендере отображаем плитку сразу в старой позиции, а на следующем тике в новой.

Источник: habr.com
К списку статей
Опубликовано: 22.01.2021 18:12:57
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка игр

Rust

2048

Gamedev

Wasm

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru