Создание прототипа Capy на Sui

Sui Capys, новый прототип Mysten Labs, служит предварительным просмотром для разработчиков и демонстрирует ключевые возможности экосистемы Sui. В этой децентрализованной игре игроки покупают, обменивают, разводят и украшают капибар, милых южноамериканских полуводных грызунов. Разработчики Sui могут адаптировать многие принципы и примеры кода, изложенные здесь, для своих собственных проектов.

Как программируемые объекты в блокчейне Sui, Capys демонстрируют такие принципы, как владение активами, возможность передачи и динамические поля объектов. Они появляются в кошельке игрока вместе с аксессуарами, включая шапки, велосипеды и шарфы. Чтобы просмотреть Capys сейчас, зайдите на Capy.art.

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

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

Отказ от ответственности

  • Это децентрализованное приложение в настоящее время считается ранней альфа-версией, поэтому оно может быть немного грубоватым.
  • Децентрализованное приложение работает на Devnet Суи, который не обладает зрелостью и стабильностью основной сети.
  • Существует известная ошибка Sui Wallet в истории транзакций при использовании Capys, и мы выпустим исправление Wallet на следующей неделе, чтобы решить эту проблему.
  • Мы планируем обновить Capy.art до стандарта адаптера кошелька в ближайшем будущем. Sui Capys — это демонстрация, созданная специально для того, чтобы вдохновить наше сообщество разработчиков.
  • Это не аирдроп. Пожалуйста, используйте кран ответственно — не спамьте наш сборщик Devnet.

Capy архитектура

Приложение Capy (Github) состоит из трех модулей: capy, capy_items и capy_market. Эти модули определяют Capys, аксессуары и торговый механизм.

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

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

Разработчики, желающие создавать игры и приложения на Sui, должны начать с определения таких основных концепций, которые будут направлять процесс разработки.

Capy Core

Модуль capy определяет основные функции Sui Capys: он определяет тип Capy, а также предоставляет издателю CapyManagerCap, открывая функции администратора для носителя. Он определяет CapyRegistry, централизованное состояние прототипа и способы его развития.

Тип: Сapy

Capy, основной тип приложения, представляет собой принадлежащий объект с определенным набором атрибутов: 32 гена и дополнительная служебная информация, необходимая для функций прототипа. У Капи есть две способности: ключ и магазин. Первый делает его доступным активом, а второй допускает бесплатную передачу и упаковку.

  • Свойство gen отмечает поколение Capy.
  • Первые Capy имеют поколение 0; новые породы имеют поколение своих родителей плюс один, то есть поколение 1, поколение 2 и т. д. Свойство src позволяет отображать изображение в проводнике.
  • Сами Capys не хранят свое полное изображение, так как оно динамическое и может быть изменено при добавлении новых элементов.
  • Свойство гены хранит последовательность генов, 32-байтовый вектор, который используется для расчета признаков и выбора генов для новорожденного во время размножения. item_count — это служебное свойство, отслеживающее количество объектов, прикрепленных к каждому Capy.
  • Свойство атрибутов хранит удобочитаемые атрибуты, созданные во время размножения. Например, { «имя»: «шаблон», «значение»: «панда» }.

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

/// The Capy itself. Every Capy has its unique set of genes,
/// as well as generation and utility information. Ownable, tradeable.
struct Capy has key, store {
id: UID,
gen: u64,
url: Url,
genes: Genes,
item_count: u8,
attributes: vector<Attribute>,
}

Тип: Capy Registry

CapyRegistry, общий объект, необходимый для разведения, хранит общее количество когда-либо родившихся Capys и содержит псевдослучайное семя, описанное ниже в разделе «Наука о генах», используемое для отбора генов во время разведения. Он содержит все определения атрибутов, присвоенные новорожденным на стадии размножения.

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

/// Every capybara is registered here. CapyRegistry acts as a source of randomness
/// as well as the storage for the main information about the game state.
struct CapyRegistry has key {
id: UID,
capy_born: u64,
capy_hash: vector<u8>,
genes: vector<GeneDefinition>
}

Тип: CapyManagerCap

CapyManagerCap — это возможность, отправляемая издателю модуля (отправителю транзакции публикации) при публикации модуля. Он разрешает действия администратора во всех модулях, включая capy_items и capy_market.

Инициализатор Capys

Это отдельное (не универсальное) приложение, поэтому их основная логика может быть запущена в инициализаторе модуля. Функция init делает две вещи:

  1. Создает CapyManagerCap и отправляет его издателю модуля.
  2. Создает и публикует CapyRegistry.

Функции администратора

Чтобы приложение стало играбельным и имело какой-то смысл, админ должен выполнить ряд действий:

  • Функция add_gene регистрирует новое GeneDefinition в CapyRegistry. При разведении все существующие в реестре атрибуты присваиваются новому Capy. Если к прототипу было добавлено новое GeneDefinition (Атрибут), Capys, родившиеся до этого добавления, не получат его, но получат его дети. Каждое определение гена имеет имя и набор селекторов, которые используются для выбора значения каждого атрибута.
  • Пакетная функция позволяет создавать партии Capys с предопределенными генами. Он используется для инициализации и на более поздних этапах для заполнения рынка дополнительными Capys для новых пользователей.

Разведение

Основной частью логики, создающей непредсказуемость и помогающей эволюции прототипа, является функция capy::breed. Эту функцию может выполнять любой игрок с двумя Капи. Логика этой функции следующая:

  1. На основе CapyRegistry.capy_hash выберите родительские гены для нового Capy.
  2. Получите список текущих GeneDefinitions из CapyRegistry и установите атрибуты.
  3. Создайте событие с новыми данными Capy. Верните новый Capy (используйте Breed_and_keep для отправки отправителю).
public fun breed(
reg: &mut CapyRegistry, c1: &mut Capy, c2: &mut Capy, ctx: &mut TxContext
): Capy {
let id = object::new(ctx);

// Update capy hash in the registry
vec::append(&mut reg.capy_hash, object::uid_to_bytes(&id));
reg.capy_hash = hash(reg.capy_hash);

// compute genes
let genes = compute_genes(&reg.capy_hash, &c1.genes, &c2.genes, GENES);
let gen = math::max(c1.gen, c2.gen) + 1;
let attributes = get_attributes(&reg.genes, &genes);
let sender = tx_context::sender(ctx);

emit(CapyBorn { /* ... */ });

// Send newborn to parents.
Capy {
url: img_url(&id),
id,
gen,
genes,
attributes,
item_count: 0,
}
}

Генная наука

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

Действительно забавная и уникальная особенность этого прототипа — возможность скрестить двух существующих Capys, чтобы получить третьего. Новый Capy наследует характеристики своих родителей.

Функция породы берет двух родительских капи и вычисляет гены новорожденного. Чтобы это действие было честным и случайным, нам нужен алгоритм выбора и начальное число. CapyRegistry предоставляет начальное значение (хранится как capy_hash) и обновляется после каждого размножения. Алгоритм следующий:

  1. Используйте хеш-функцию (sha3_256) три раза с солью, чтобы сгенерировать три вектора по 32 байта (отмеченных как A, B и C), полученных из capy_hash. Используйте первый вектор (A) для выбора родительского гена. Если значение N-го байта больше 126, выберите ген первого родителя. В противном случае выберите ген второго родителя. Как показано на диаграмме выше, первый ген будет P2, второй P1, третий P2 и снова четвертый P1 (до N=32). Второй вектор (В) определяет вероятность мутации.
  2. Если значение в позиции N больше 250, используйте ту же позицию в третьем векторе ©, чтобы выбрать значение для мутации. В этом примере третий ген будет мутировать и его значение будет равно 42.
/// Computes genes for the newborn based on the 'random' seed r0, and parents' genes.
///
/// The `max` parameter affects how many genes should be changed.
/// For example, if the number of genes is 32 but only 4 Attributes defined in the
/// registry, the `max` should be set to 4. Remainder genes should not mutate.
fun compute_genes(r0: &vector<u8>, g1: &Genes, g2: &Genes, max: u64): Genes {
let i = 0;

let s1 = &g1.sequence;
let s2 = &g2.sequence;
let s3 = vec::empty();

let r1 = derive(r0, 1); // for parent gene selection
let r2 = derive(r0, 2); // chance of random mutation
let r3 = derive(r0, 3); // value selector for random mutation

while (i < max) {
let rng = *vec::borrow(&r1, i);
let gene = if (lor(rng, 127)) {
*vec::borrow(s1, i)
} else {
*vec::borrow(s2, i)
};

// There's a tiny chance that a mutation will happen.
if (lor(*vec::borrow(&r2, i), MUTATION_CHANCE)) {
gene = *vec::borrow(&r3, i);
};

vec::push_back(&mut s3, gene);
i = i + 1;
};

Genes { sequence: s3 }
}

Capy предметы

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

/// Wearable item. Has special display in capy.art application
struct CapyItem has key, store {
id: UID,
url: Url,
type: String,
name: String,
}

Управление элементами

При добавлении и удалении аксессуаров в Capys используются поля динамических объектов, более эффективная и удобная замена родительско-дочерним объектам в Sui. Динамические поля допускают произвольные имена и могут добавляться и удаляться на лету.

Следующий код добавляет элемент в Capy:

entry fun add_item<T: key + store>(capy: &mut Capy, item: T) {
emit(ItemAdded<T> {
capy_id: object::id(capy),
item_id: object::id(&item)
});

dof::add(&mut capy.id, object::id(&item), item);
}

Capy Маркет

Чтобы приобретать и продавать Capys и CapyItems, мы создали Capy Market. Этот модуль использует поля динамических объектов и блокирует предметы, чтобы их можно было приобрести после оплаты их цены. В этой архитектуре торговой площадки один объект торговой площадки существует для каждого типа элементов (CapyMarket<Capy> продает Capys, а другой объект, CapyMarket<CapyItem>, продает аксессуары Capy), списки прикрепляются к торговой площадке в виде полей динамических объектов, а перечисленные объекты прикреплены к спискам.

  • → Листинг → T CapyMarket<T> + → Листинг → T + → Листинг → T

Торговая площадка и функция списка

/// A generic marketplace for anything. T marks the type for Listings.
struct CapyMarket<phantom T: key + store> has key { id: UID }

/// A listing for the marketplace. Contains the price and owner of the Listing.
struct Listing<phantom T: key + store> has key, store {
id: UID,
price: u64,
owner: address
}Каждый экземпляр Marketplace обслуживает только один тип. В этом приложении один экземпляр Marketplace существует для типа Capy, а другой — для типа CapyItem.

Функция списка использует динамические поля. Он делает перечисленный элемент полем листинга, а затем делает листинг полем CapyMarket.

/// List a new item on the `CapyMarket`.
public entry fun list<T: key + store>(
market: &mut CapyMarket<T>,
item: T,
price: u64,
ctx: &mut TxContext
) {
let id = object::new(ctx);
let item_id = object::id(&item);
let owner = tx_context::sender(ctx);

emit(ItemListed<T> { /* ... */ });

// First attach Item to the Listing with a boolean `true` value;
// Then attach listing to the marketplace through `item.id`;
dynamic_object_field::add(&mut id, true, item);
dynamic_object_field::add(&mut market.id, item_id, Listing<T> {
id, price, owner
});
}

Capy Вдохновение

Мы создали прототип Capy, чтобы продемонстрировать некоторые ключевые функции Sui и вдохновить разработчиков на их собственные проекты. Capys использует объектно-ориентированный характер Sui, позволяя игрокам использовать переносные аксессуары, которые игроки могут продавать и покупать, а также создавать новые Capys на основе родительских атрибутов. Мы разработали прототип с возможностью бесконечного расширения, чтобы он порадовал как пользователей, так и разработчиков.

Мы надеемся, что приведенные здесь примеры и код окажутся полезными для разработчиков Sui. Несколько реализаций, которые должны выделиться, включают Capy Market, Accessories и Breeding. Capy Market служит моделью для любого механизма торговли или хранения. Аксессуары показывают хорошее использование динамических полей. Разведение предлагает уникальные средства автоматического создания новых объектов с бесконечными приложениями.

Удачи и удачного развития!

Created by wonder.jpiggy#7100

--

--