Sneekie

Від GW-BASIC до JavaScript, поруч

Код 1988 року ліворуч, порт 2026 року праворуч — і як зібраний новий шар під ним.

Вебверсія не є переписуванням — це вірний порт. Ігрова логіка перекладена з SNEEKIE.BAS оператор за оператором, з оригінальними назвами змінних і навіть номерами рядків BASIC у коментарях. Насправді змінився тонкий шар під грою: те, що GW-BASIC давав безкоштовно — памʼять екрана, клавіатуру, звук, GOTO — довелося відбудувати для браузера. Ця сторінка ставить поруч показові фрагменти і пояснює цей шар; для кожного рядка BASIC є сторінка Пояснення.

Старий зелений екран Sneekie зʼєднаний пікселями із сучасною браузерною версією.

Міст: екран і є структура даних

GW-BASIC записував символи прямо у текстову памʼять екрана PC 80×25 через POKE і читав їх назад через PEEK, щоб дізнатися, що де стоїть. Окремого обʼєкта "змія" чи "стіна" не було — зображення було станом. Порт зберігає цю модель точно: 4000-байтовий Uint8Array (vram), два байти на клітинку, символ і атрибут, адресуються так:

offset = (рядок − 1) × 160 + (стовпець − 1) × 2

Оскільки модель однакова, ігровий код майже не змінюється — слова POKE і PEEK лише стають функціями poke() і peek(). Усе інше на цій сторінці випливає з відбудови решти runtime GW-BASIC навколо одного спільного масиву.

Що довелося відбудувати

Механізм 1988 рокуЗаміна 2026 року
POKE / PEEK у відеопамʼять на &HB000/&HB800 poke() / peek() у 4000-байтовому Uint8Array
LOCATE r,c : PRINT CHR$(n) locate(r,c); pc(n) — той самий курсор, ті самі коди символів
екран оновлює себе сам (це і є відеопамʼять) набір брудних клітинок + requestAnimationFrame, glyphs з вбудованого шрифту IBM CP437
INKEY$ / INPUT$ блокують до клавіші async + Promise-буфер клавіатури (keyOrTimeout/waitKey)
SOUND freq, ticks Web Audio square-wave oscillator на тому самому годиннику 1/18.2 с
ON LEVEL GOSUB … dispatch-масиви CFG[] і ENEMY[]
GOTO 510 / RETURN 510 (смерть) throw DEATH, перехоплений навколо циклу руху
змінні (DEFINT A-Y, нідерландські назви) ті самі назви збережені буквально (T, BTEL, HART, KLAVER…)

Одна глибока зміна: блокування стає async

GW-BASIC ішов згори вниз і просто зупинявся на INKEY$/INPUT$, доки ви не натиснете клавішу. Вкладка браузера не може так блокуватися — тому точки очікування стали await, а playLevels() і program() стали async. Малий Promise-буфер клавіатури замінює BIOS key buffer, а оскільки GOTO у JavaScript немає, рядки "вистрибнути і померти" стають кинутим сигналом DEATH, перехопленим навколо циклу. Ці два прийоми — await для вводу, throw для смерті — дозволяють решті коду лишатися буквальним перекладом.

Блокова змія Sneekie стрибає крізь піксельний портал зі старого CRT у сучасний браузер.