Към съдържанието

PlayBelote v2

3D белот, написан наново без поглед в стария код: механа с герои, джаз салон и маса във Web Worker

Роля
Собственик на продукта, ръководя екипи от агенти
Статус
Непубликуван
Код
Частно хранилище
Технологии
TypeScriptVitethree.jsWeb WorkerzodBlenderVitestPlaywrightfast-check
Раздаване на белот в 3D темата „Механа“: бялокос мъж с каскет, жена с червена къдрава коса в бяла блуза с бродерия и мъж с тъмен каскет седят на маса с червено-бяла карирана покривка, с картите на текущата ръка върху покривката и трите оставащи карти на играча долу. Текстът на интерфейса е на български.
По средата на раздаване в темата „Механа“, с резултата и обявената игра в горните ъгли.

В числа

16/ 16

емблематични моменти във всяка стая, задействани от истински събития в играта, от край до край

999/ 1 000

мача, спечелени от базовия бот срещу играч със случайни допустими ходове

438 430

недопустими хода, подадени на модула с правилата в 2 000 игри с fuzz тест, 0 изключения

136

записани решения по дизайна, всяко с алтернативите, които са отпаднали

144,1fps

масата в механата в момент, когато играчът решава, нагоре от 37,5

Проблемът

PlayBelote v1 растеше с промени почти година и клиентската му част се чупеше, щом някое събитие дойдеше два пъти или не по ред, както показа грешката при превключване с alt-tab. Исках втора игра, която запазва правилата на първата, но не и допусканията ѝ, започва като игра за един играч и слага картите в стаи, които си струва да се гледат.

Подходът

Написах задание с избрания набор от технологии, правилото clean-room и критерии за приемане, фиксирани преди всяка фаза, и пуснах по него агенти под мое ръководство. Заданието отхвърли писмено осем набора от технологии още преди да е построено каквото и да било, сред тях Colyseus, Phaser, React Three Fiber и Godot. Агент-оценител, който не беше писал кода, пускаше командите и докладваше резултатите, а всяко решение влизаше в дневник заедно с алтернативите, които е изместило.

Масата в механата в началото на играта: близки изгледи на тримата седнали герои, коза и глинен съд зад тях, чинии с храна върху карираната покривка и осемте карти на играча, разперени по долния край, като на всяка фигурна карта има български народен герой.
Началото на играта, по-близо до героите.

Как работи

  1. Две стаи, общ код за изобразяване

    Салонът е картова зала в арт деко по модел на старото градско казино в София: полунощно зелено сукно с видими влакна, месингов парапет и една топла лампа, чието петно светлина следва играча на ход. Механата е маса в механа под асма, с карирана покривка, печка на дърва и Рилските езера в прозореца. Картите са тънки двустранни 3D обекти, които летят по дъга, обръщат се и хвърлят сенки под лампата, а всяка преследва целевото си място като на затихваща пружина, затова раздадена карта се установява на мястото си, като първо го превишава съвсем малко. Всеки шейдър е написан от нулата.

  2. Герои със задачи

    В механата баба раздава и коментира, бабите на пейката следят играта и мъмрят за истинските груби грешки, които ботът им отбелязва, съседът наднича през оградата, а Бора, шар пей, хърка под масата и се събужда при големите моменти. Козата е игра на думи с „коз“ и „коза“: при обява „без коз“ козата блее и си тръгва. При контра нахлува кукер със звънци, а при капо идва целият сватбарски оркестър, хоро около масата и Бора, която вие заедно с кларинета. Във всяка стая има по 16 емблематични момента. Героите първо бяха плоски изрезки и изглеждаха с дефекти до осветената маса, затова по средата на разработката станаха модели от Blender, компресирани с KTX2 и meshopt до общо 44 MB.

  3. Музика, която следи резултата

    Цялата музика се синтезира в JavaScript от планировчик на тактове, който планира напред във времето, без нито един записан звук, и служи същевременно за измервател на серията: всяка поредна победа добавя пласт. В Салона оригинална модална хард боп мелодия добавя уокинг бас, ride чинел и четки, после пиано. В Механата оригинална ръченица в 7/8 добавя тъпан, гайда, кларинет и акордеон до пълния състав, броячът на точките тиктака в същото 7/8, а решаващото раздаване преминава към 11/16 на копаницата. Гласовете на героите са формантен синтез на български и английски, а регулатор на интензивността (спокойно, оживено, сватба) усилва или намалява частиците, реакциите и музиката.

  4. Масата като актьор във Web Worker

    Цялото състояние на играта и часовниците ѝ живеят в актьор „маса“ във Web Worker. Клиентската част говори с него през протокол, проверяван със zod, с пореден номер на съобщенията, събития със скрити за всяко място данни и, при повторно свързване, снимка на състоянието плюс пропуснатите събития. Играта се определя от началното си число (seed) и дневника на действията, затова същата маса по-късно може да стои зад мрежова връзка. Скриването на раздела и връщането към него, презареждането или рестартирането на браузъра по средата на ръка възстановяват играта до точния изглед на актьора.

  5. Ботове с търсене и точно решаване на финала

    Ботовете търсят с детерминизация върху до 48 случайно избрани възможни светове (разпределения на скритите карти) за всяко решение, с едни и същи случайни числа за всички кандидат-ходове и евристични симулации до края. Щом решаващият играч остане с две карти или по-малко, минимаксът решава раздаването точно. ISMCTS го разгледах при проучването на технологиите, но ботовете използват детерминизираното търсене. Базовият бот спечели 999 от 1 000 мача срещу играч със случайни допустими ходове, при средно 5,58 ms на решение.

  6. Проверки преди приемане, дневник на решенията и бюджет на кадър

    Критериите за приемане бяха фиксирани преди всяка фаза. Покрити бяха всичките 112 идентификатора от спецификацията, fuzz тест подаде на модула с правилата 2 000 игри и 438 430 недопустими опита с 0 изключения, а 56 снимки на екрана провериха оформлението при 20 размера на екрана от 320 на 480 до 3840 на 2160. В момент, когато играчът решава, масата в механата скочи от 37,5 на 144,1 fps, а изтеглянето ѝ на компютър спадна от 54,8 MB на 19,4 MB.

Запитване за комбинация в темата „Механа“: златен бутон „Терца“ и бутон „Без обява“ вдясно, две карти върху карираната покривка и ръката на играча, разперена по долния край.
Комбинация терца, три поредни карти.

Какво избрах и какво отпадна

Избрах

Първо игра за един играч, с канал за връзка, който може да се смени по-късно

Пред

Игра за много играчи още от първата стъпка

Исках играта за един играч да е готова, преди да пипам каквато и да е мрежова част. Протоколът на масата по-късно минава от postMessage към WebSocket, без да се променя играта.

Избрах

Истински 3D модели

Пред

Плоски герои-изрезки

Изрезките изглеждаха с дефекти и не на място до осветена 3D маса.

Избрах

Компресирани модели, направени от неизменени оригинали от Blender

Пред

Компресиране на готовите файлове на място или геометрична компресия Draco

Meshopt се декодира по-бързо, а three.js носи декодера си вграден. Оригиналите остават байт по байт непроменени, затова проверка може да кодира наново всеки модел и да потвърди, че пуснатите файлове съвпадат: 36 еднакви, 0 различни.

Избрах

Clean-room разработка по писаните правила

Пред

Пренасяне на кода от v1

Преизползването на v1 би пренесло старите допускания във втората игра.

Резултатът

Първите три фази минаха проверката преди приемане в третия кръг, със 717 теста в 63 файла, lint без забележки и преминаващи проверки на типовете в 8 от 8 проекта. Проверката за производителност без забавяне стои в отделен клон и мина 6 от 9 критерия: масата в Салона още показва най-лош кадър от 69,3 ms при бюджет от 50 ms, а заместителят на телефон, процесор, забавен 6 пъти, не минава. Не е тестван истински телефон: играта на телефон е само емулирана. Играта няма отдалечено хранилище и публична версия, а при предаването още не бях играл завършената версия.

Какво следва

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