Полюйте на Wumpus


39

Коли я був хлопцем, діти блукали в комп’ютерних магазинах і грали в «Полювання на Wumpus», поки персонал нас не вигнав. Це була проста гра, запрограмована на домашніх комп’ютерах середини 1970-х, машини настільки рудиментарні, що замість мікропроцесорів розміром з курячими, я думаю, що деякі з них, мабуть, мали справжні курячі.

Давайте спричинимо цю минулу еру, відтворивши гру на сучасному обладнанні.

  1. Гравець починається у випадковій кімнаті на ікосаедричній карті (таким чином, усього 20 номерів, з'єднаних між собою, як грані ікосаедра, і кожна кімната має рівно три виходи).

  2. Вимпел починається у випадково вибраній іншій кімнаті. Вимпел смердить, і його запах можна виявити в будь-якій з трьох кімнат, що прилягають до його розташування, хоча гравець не може визначити напрямок запаху. У грі повідомляється лише про те, що "ти відчуваєш запах вум".

  3. Гравець несе лук і нескінченну кількість стріл, які він може стріляти в будь-який момент в кімнату перед собою. Якщо в тій кімнаті знаходиться вімп, він гине і гравець виграє. Якщо вімп не був у цій кімнаті, він вражається і переміщається випадковим чином у будь-яку з трьох кімнат, підключених до його поточного місця розташування.

  4. Одна, випадково вибрана кімната (гарантовано не буде кімнатою, в яку починає гравець) містить бездонну яму. Якщо гравець перебуває в будь-якій кімнаті, що прилягає до ями, він відчуває вітер, але не має поняття, з яких дверей прийшов вітерець. Якщо він заходить в кімнату з ямою, він помирає і виграє виграш. Вим’я не впливає на ямку.

  5. Якщо гравець заходить до кімнати вумпуса, або якщо wumpus заходить до кімнати гравця, wumpus виграє.

  6. Гравець визначає напрямок, до якого він стикається, з числом (1 = праворуч, 2 = ліворуч, 3 = назад), а потім дія (4 = стріляйте стрілкою, 5 = ходіть у вказаному напрямку).

  7. Для того, щоб забити, кожен ігровий рядок ("Ви відчуваєте вітер", "Ви відчуваєте запах вумби", "Ваша стрілка нічого не вдарив" тощо) можна вважати одним байтом. Не зловживайте цим, щоб приховати ігровий код у тексті; це лише для взаємодії з гравцем.

  8. Відрахуйте 10% від вашої кількості байтів для впровадження мегабатів, які починаються у випадковій кімнаті, відмінній від плеєра (хоча вони можуть ділити кімнату з wumpus та / або ямою). Якщо гравець заходить в кімнату з кажанами, кажани перевезуть гравця до іншої випадково вибраної кімнати (гарантовано, що це не кімната з ямою або ворсом у ній), перш ніж вилетіти у своє, нове випадкове місце розташування. У трьох кімнатах, що прилягають до кажанів, їх чути писк, але гравцеві не дано інформації про те, з якої кімнати надходить звук.

  9. Відрахуйте 35% від вашої кількості байтів для впровадження графічного інтерфейсу, який показує ікосаедричну карту та якесь вказівку інформації, яку гравцем досі надано, про розташування ями, вовчака та кажанів (якщо це застосовно) стосовно гравець. Очевидно, що якщо вімп рухається або гравець переміщається битами, то карту потрібно скинути відповідно.

  10. Найнижча кількість байтів, якщо налаштовано, виграє.

БАЗОВИЙ вихідний код для версії гри (не обов'язково відповідний вищевикладеним правилам і, у будь-якому випадку, абсолютно неперероблений) можна знайти на цьому веб-сайті та, ймовірно, інших.


Деякі роз’яснення: 3. якщо вумп не був у тій кімнаті, він здивовано і переміщується до однієї з ТРИХ кімнат .. тож якщо ви стріляєте зі стріли і не пропустите, то вімп може прийти і вбити вас, правда? І вімп рухатиметься лише при здивованому стані, інакше він просто залишається поставленим? 6. Я розумію, що заголовок гравця визначається номером, з якого він прийшов. Тож якби він прийшов з півдня, його варіанти були б 1. північний схід 2. північний захід 3. південь, а якби він прийшов з півночі, було б навпаки. Також ваші правила здаються простішими / гравцями, ніж референтна програма (яку я ще не детально досліджував.) Чи правильно я?
Річка Рівня Св.

Арг! Я не можу знайти жодного зображення подвійного графіка ікосаедра ніде в мережі.
Джек М

1
@steveverrill Так, якщо ви його розпустіть, він може прийти і вбити вас. Якщо ви не бачите його, він не рухається. У грі багато варіацій; багато версій дозволяють, наприклад, стрілкам кружляти навколо і вбивати вас. Я це вигадав.
Майкл Стерн

3
@JackM карта граней ікосаедра ідентична карті вершин додекаедра, і цей графік легко знайти. Спробуйте, наприклад, wolframalpha.com/input/?i=Dodeca CathedralGraph+edgerules або еквівалентну команду Mathematica GraphData ["DodecahedGraph", "EdgeRules"]. У будь-якому випадку ви отримаєте {1 -> 14, 1 -> 15, 1 -> 16, 2 -> 5, 2 -> 6, 2 -> 13, 3 -> 7, 3 -> 14, 3 -> 19, 4 -> 8, 4 -> 15, 4 -> 20, 5 -> 11, 5 -> 19, 6 -> 12, 6 -> 20, 7 -> 11, 7 -> 16, 8 -> 12, 8 -> 16, 9 -> 10, 9 -> 14, 9 -> 17, 10 -> 15, 10 -> 18, 11 -> 12, 13 -> 17, 13 -> 18, 17 -> 19, 18 -> 20}
Майкл Стерн

2
@JackM Ні, "назад" має на увазі повернутись та піти назад шляхом, яким ви прийшли. Якщо двічі натиснути «назад», ви опинитесь там, де почали. Не потрібно зберігати попередні стани гри.
Майкл Стерн

Відповіді:


21

GolfScript, 163

:n;:`"You shot the wumpus.
""The wumpus ate you.
""The pit swallowed you.
"{19:|rand}2*0|{[:,~,4%"ftvh"=.,+,@-]{20%}%}:^{;.^.+.3$?>"You feel a breeze.
"1$6"You smell a wumpus.
"4$8{$?-1>*p}2*'"#{'|):|';`head -1`}"'++~{3%}/={=3$=|{"Your shot missed.
"p@^3rand=@@}if}{=@;}if.[|4$6$]?.)!}do])=

Оцінка отримується шляхом підрахунку байтів (290), додавання кількості рядків, використовуваних для взаємодії з користувачем (6), і віднімання комбінованої довжини цих рядків (133). Посилання рядків є частиною рядків і сприяють підрахунку байтів.

Віхи

  1. Перенесена відповідь професорфіш від Баша на GolfScript. Оцінка: 269

  2. Діяв за пропозиціями Пітера Тейлора в коментарях. Оцінка: 250

  3. Пітер Тейлор відремонтував весь мій код і допоміг мені стиснути таблицю пошуку. Оцінка: 202

  4. Замінив таблицю пошуку сусідніх кімнат математичним підходом. Оцінка: 182

  5. Відновлений вхід, вихід і функція, що підтримує математичний підхід. Оцінка: 163

Велике «Дякую!» Йде до Пітера Тейлора за всю його допомогу.

Як це працює

20 номерів представлені у вигляді вершин додекаедра, яким присвоєні числа від 0 до 19 наступним чином:

Додекадральний графік

Щоб знайти кімнати, що примикають до кімнати N, та замовити їх за годинниковою стрілкою, ми повинні розглянути чотири випадки:

  • Якщо N ≡ 0 mod 4 (сині вершини), сусідня кімната - 19 - N , N + 2 mod 20 і N - 2 mod 20 .

  • Якщо N ≡ 1 mod 4 (зелені вершини), сусідня кімната - 19 - N , N - 4 mod 20 та N + 4 mod 20 .

  • Якщо N ≡ 2 mod 4 (жовті вершини), сусідня кімната - 19 - N , N - 2 mod 20 та N + 2 mod 20 .

  • Якщо N ≡ 3 mod 4 (червоні вершини), сусідня кімната - 19 - N , N + 4 mod 20 і N - 4 mod 20 .

# The function “p” is implemented as “{`print n print}”. By storing an empty string in 
# “n” and nullifying “`”, “p” becomes an alias for “print”.

:n;:`

# Push the messages corresponding to the three possible outcomes of the game.

"You shot the wumpus.\n""The wumpus ate you.\n""The pit swallowed you.\n"

# Place the wumpus and the pit in randomly selected rooms different from room 19; place 
# the player in room 19, with his back to room 0.

{19:|rand}2*0|

# Function “^” takes a single number as its argument and returns an array of all the
# adjacent rooms to the room that number corresponds to.

{

  [

    :,~       # Store the room number in “,” and negate it ( ~N ≡ 19 - N mod 20 )

    ,4%       # Push the room number modulus 4.

    "ftvh"=   # If it is equal to 0|1|2|3, push 102|116|118|104 ≡ 2|-4|-2|4 mod 20.

    .,+,@-    # Determine the room number plus and minus the integer from above.

  ]{20%}%     # Take all three room numbers modulus 20.

 }:^

{             # STACK: Strings Pit Wumpus Previous Current Function|Index

  ;           # STACK: Strings Pit Wumpus Previous Current

  # Find the adjacent rooms to the current room, duplicate them and remove the rooms 
  # before the first occurrence of the previous room. Since the rooms are ordered in
  # clockwise fashion, the array of adjacent rooms will begin with the rooms 
  # corresponding to the following directions: “Back Left Right”

  .^.+.3$?>   # STACK: Strings Pit Wumpus Previous Current Adjacent

  # Push two more messages and their respective triggers.

  "You feel a breeze.\n"1$6"You smell a wumpus.\n"4$8

  # STACK: ... Pit Wumpus Previous Current Adjacent String Adjacent 6 String Adjacent 8

  # Do the following twice: Duplicate the nth stack element and check if it's present in 
  # the array of adjacent rooms. If so, print the string below it.

  {$?-1>*p}2*

  # Read one line (direction, action, LF) from STDIN. The counter “|” is needed so the 
  # result won't get cached.

  '"#{'|):|';`head -1`}"'++~

  {3%}/       # Replace 1|2|3|4|5|LF with their character codes modulus 3 (1|2|0|1|2|1).

  ={          # If the player shoots an arrow:

    =3$=      # Determine the specified room and check if it corresponds to the wumpus.

      |       # If it does, push and invalid room number ( | > 19 ).

      # If it does not, say so and move the wumpus to a randomly selected adjacent room.

      {"Your shot missed."p@^3rand=@@}

    if

  }{           # If the player moves:

    =@;        # Place him into the selected room.

  }if

  # STACK: Pit Wumpus Previous Current Invalid?

  # Determine if the player's current room number is either invalid, the wumpus's room
  # number or the pit's room number (first match).

  .[|4$6$]?

  # If there is no match, the index is -1 and incrementing and negating it yields “true”.

  # STACK: Strings Pit Wumpus Precious Current Invalid? Index Boolean

# Repeat loop is the boolean is falsy. If repeated, the first instruction of the loop 
# will pop the index.

}do      

# Consolidate the entire stack into an array. And pop its last element: the index.
# Replace the array with the element corresponding to that index.

])=

# GolfScript will execute “print n print”.

1
Ви можете зберегти 1 в за Qдопомогою 19rand 97+; 2 в @с 97%3*&>..., ще один на вбудовування , Qяк {19rand 97+}2*:,\:H, деякі з них, замінивши |з *, який часто є кращим способом зробити if. Bне має жодної мети, і я думаю, що ще кілька змінних можна було б усунути за допомогою стека.
Пітер Тейлор

1
Забув згадати ще одну частий трюк: перетворення бази для таблиць пошуку. Ви можете замінити 62 символи для списку суміжності 33-знаковим рядком, за яким слід 256base 20base(і, ймовірно, також усунути кілька +/- 97). Єдиним недоліком є ​​те, що для цього знадобляться символи, що не друкуються.
Пітер Тейлор

1
Я врятував ще 13 шляхом рефакторингу на більш ідіоматичний GS (в основному використовуючи стек, а не змінні); і є додаткові 10, щоб зробити результат менш гарним. Це крім стиснення таблиці пошуку, згаданого в моєму попередньому коментарі.
Пітер Тейлор

1
Зовсім не мені сподобалося. Я просто розчарований, що підхід до таблиці пошуку набагато кращий, ніж той математичний, який я мав намір використати. До речі, я вважаю, що у вашій поточній версії є невеликий помилок, тому що якщо ви стріляєте зі стріли, пропускаєте та пускаєте в кімнату вімп, то він видається лише You were killed by the wumpusбез згадки про відсутність стрілки. Тому я додав у не дуже гарній версії.
Пітер Тейлор


15

REV0 C ++ (Visual Studio на Windows) 405

#include"stdafx.h"
#include<stdlib.h>
#include<time.h>
int main(){srand(time(NULL));char i,h=rand()%19,w=rand()%19,p=19,d=0,q,e,m[]="e@LwQMQOSOLT";while(p-h&&p-w){for(i=3;i--;){q=(p+m[p%4*3+i])%20;if(q==w)puts("you smell a wumpus");if(q==h)puts("you feel a breeze");}scanf_s("%d",&i);e=(d+i/10)*m[p%4]%3;q=(p+m[p%4*3+e])%20;if(i%5){if(q==w){puts("YOU KILLED THE WUMPUS!");h=p;}else{puts("arrow missed");w=(w+m[w%4*3+rand()%3])%20;}}else{p=q;d=e;if(p==h)puts("YOU FELL IN A HOLE!");}if(p==w)puts("THE WUMPUS GOT YOU!");}}

Нижче наводиться перегляд, що демонструє, що (за умови правильного запуску поруч із небезпекою) при правильній грі ви завжди можете перемогти. Гравець відчуває вітер, повертається назад і робить повну петлю проти годинникової стрілки. Оскільки йому потрібно рівно 5 ходів, щоб знову відчути вітер, він знає отвір праворуч і потрапляє якомога далі. Точно так само, коли він відчуває запах вум, не знаючи, чи це справа, чи ліворуч, він повертається назад і робить петлю за годинниковою стрілкою. Потрібно йому 5 рухів, щоб знову понюхати ворс, тож він знає, що це зліва і стріляє з певністю.

Якби він зациклився на іншому шляху, він би швидше знайшов бляк і знав, що це було в тому ж напрямку, в якому він повертався.

введіть тут опис зображення

REV1 C (GCC на Cygwin), 431-35% бонус = 280,15

#define u(t,s,c) if(t){puts(s);c;}
i,d,e,a,b;main(){srand(time(0));char q,p=19,h=rand()%p,w=rand()%p,*m="e@LwQMQOSOLT-\\/\n \v ";  
while(p-h&&p-w){
  for(i=3;i--;){q=(p+m[p%4*3+i])%20;u(q==w,"you smell a wumpus",a|=2<<p)u(q==h,"you feel a breeze",b|=1<<p)}
  for(i=20;i--;)printf("%c%c",i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),m[i%4+15]);
  scanf("%d",&i);e=(d+i/10)*m[p%4]%3;q=(p+m[p%4*3+e])%20;
  if(i%5){u(q-w,"arrow missed",w=(w+m[w%4*3+rand()%3])%20;a=0)else u(1,"YOU KILLED THE WUMPUS!",h=p)}
  else{p=q;d=e;u(p==h,"YOU FELL IN A HOLE!",)}
  u(p==w,"THE WUMPUS GOT YOU!",)}}

Нові рядки додані для наочності. Зміни від Rev 0 такі:

Велика подяка @Dennis за рекомендацію компілятора GCC на емуляторі Cygwin Linux для Windows. Цей компілятор не вимагає includes в програмі rev 0, і він дозволяє intтип змінних за замовчуванням для змінних, і main.це підказка для гольфу, що змінюється!

Крім того, що працює в Linux означає, що \fзмушує курсор переміщатися вниз, не роблячи повернення каретки (на відміну від Windows, де він просто видає символ для друку.) Це дозволило значно скоротити операцію printf, яка друкує плату

Кілька додаткових порад від Денніса в коментарях, і один із моїх: зміна стану при перевірці, чи стрілка потрапила на вімп: if(q==w)> if(q-w)(..else .. є зворотним)

Додавання графічного дисплея, що показує інформацію, яку гравець знає про те, де пахне вовчак / вітер, як вимагає 35% бонусу. (Я видалив стару версію налагодження цієї версії, яка показала точне положення wumpus and hole. Це можна побачити в історії редагування.)

REV2 C (GCC на Cygwin), бонус 389-35% = 252,85

#define Q(N) (N+"QTLOQMQOSOLT"[N%4*3+e])%20
#define P printf(
i,d,e,a,b;main(){int p=19,q=srand(&p),h=rand()%p,w=rand()%p;
while(p-h&&p-w){
  for(e=3;e--;){q=Q(p);q-w||P"You smell a wumpus\n",a|=2<<p);q-h||P"You feel a breeze\n",b|=1<<p);}
  for(i=20;i--;)P"%c%c",i-p?48+(a>>i&2)+(b>>i&1):"-\\/"[d],"\n \v "[i%4]);
  scanf("%d",&i);e=(d+i/9)*"edde"[p%4]%3;q=Q(p);
  if(i%5){e=rand()%3;w=q-w?P"Your arrow didn't hit anything\n",a=0)&Q(w):(p=20);}
  else p=q,d=e;
}
P p-20?p-w?"YOU FELL IN A HOLE!\n":"THE WUMPUS GOT YOU!\n":"YOU KILLED THE WUMPUS!\n");}

Ще раз дякую Деннісу за те, що переробили код:

Константа Char m[]замінюється літералами (я не знав, що ти можеш індексувати літерал.)

Висівання випадкових чисел зі змінною стека (залежно від системи, деякі системи рандомізують розподіл пам'яті як міру безпеки.)

Макрос із putsзаміненим на макрос із printfта додатковим кодом, який повинен бути виконаний, коли повідомлення відображається розміщеним усередині printfаргументів (перевага, яке має особа, що printf не друкує останні кілька аргументів, якщо в рядку формату недостатньо специфікаторів формату.) ifзамінено на||

Розрахунок нової позиції гравця / wumpus, розміщеної всередині нового макроса.

Виграти / програти повідомлення, розміщені поза whileциклом. ifзамінений умовним оператором.

Використання умовного оператора в рядку для стрільби з стрільби. Якщо програвач не вистачає, для цього потрібно як надрукувати повідомлення, так і відрегулювати положення wumpus. Денніс запропонував декілька способів поєднання printfта обчислення позиції wumpus в єдиний вираз, але я пішов одним із власних. printfповертає число надрукованих символів, які для Your arrow didn't hit anything\nна 31 (11111 двійковим.) Отже, 31&Q(w)==Q(w).

Мій інший внесок у цю редакцію - це усунення непотрібних дужок.

Вихідні дані

Тут гравець уже знайшов, де знаходиться Wumpus, але вирішив зробити ретельне дослідження, щоб з’ясувати, де саме знаходиться яма. На відміну від моєї старої версії налагодження, в якій було показано, де в грі знаходилися вовчак та яма, тут показано лише кімнати, де гравець побував і відчув, що вітерець (1) пахнув вовком (2) або обом (3). (Якщо гравець стріляє зі стрілки і пропускає, змінна, aщо містить інформацію про положення wumpus, скидається.)

введіть тут опис зображення

ПРЕДСТАВЛЕННЯ ІКОСАХЕДРОНУ

Примітка. Цей розділ базується на версії 1

Моя зірка! У моєму коді немає графіка. Щоб пояснити, як це працює, дивіться мапу світу нижче. Будь-яка точка ікосаедра може бути представлена ​​широтою 0-3 та довготою 0-4 (або єдиним числом long*4+lat.) Лінія довготи, позначена на карті, проходить лише через ці грані з нульовою довжиною, і лінія широти проходить через центр граней з нульовою широтою.

Гравець може орієнтуватися на 3 можливі осі, представлені символами наступним чином: північ-південь -північний схід-південний \захід північний захід-південний схід /. У будь-якій кімнаті він має рівно один вихід на кожну з цих доступних йому осей. На показаному дисплеї програвач робить повний цикл за годинниковою стрілкою. Зазвичай гравця легко визначити, відзначивши гравця, звідки він прийшов, а отже, куди йому дозволено їхати.

Один випадок, який трохи непростий для неосвіченого ока, - четвертий. Коли ви бачите нахил в одному з цих полярних рядів, гравець вийшов з полярної клітини, найближчої до зовнішнього кінця косого, і, як правило, звернений до екватора. Таким чином, гравець виходить на південний схід, і його варіанти: 15 (ПІВДЕНЬ, клітина праворуч) 25 (northEAST, клітина вгорі) або 35 (northWEST, комірка внизу.)

Отже, я в основному відображаю ікосаедр до сітки 5х4 з осередками, пронумерованими від 19 до 0, у порядку їх друку. Хід робиться додаванням або відніманням від поточної позиції, залежно від широти та напрямку гравця, за таблицею нижче.

Якщо гравець відходить від нижньої (західної) дошки, він повертається на верхню (східну) сторону і навпаки, тому його позиція приймається за модулем 20. Як правило, ходи кодуються в m [], додаючи ascii 80 ( P) до вихідного значення, що дає символи, показані нижче, але принцип будь-якого кратного 20 може бути доданий, не впливаючи на операцію.

Table of addition values for moves

Direction Symbol Latitude 0  1  2  3     Latitude 0 1 2 3

0, N-S      -             1 -1  1 -1              Q O Q O  
1, NE-SW    \            -4  1 -1  4              L Q O T
2, NW-SE    /             4 -3  3 -4              T M S L

Вхід гравця (розділений на 10, щоб видалити другу цифру) додається до його поточного напрямку та береться за модулем 3, щоб отримати його новий напрямок. У більшості випадків це добре працює. Однак існує проблема, коли він знаходиться в полярній кімнаті і рухається до полюса. При складанні карти нижче буде зрозуміло, що якщо він вийде з кімнати, що виходить на "північний схід", він увійде в новий квадрат, що виходить на "південний схід", тому необхідно внести виправлення. Це робиться в рядку e=(d+i/10)*m[p%4]%3;на множення на m[p%4]. Перші чотири значення m [] вибираються такими, що крім своєї функції вище, вони також мають характеристику m[1]%3==m[2]%3==1і m[0]%3==m[3]%3==2. Це залишає напрямок лише для екваторіальних кімнат і застосовує необхідну корекцію для полярних кімнат.

Логічним часом для виправлення буде після переїзду. Однак для збереження символів це робиться до переміщення. Тому певні значення в m [] повинні бути перенесені. Отже, останні 2 символи LTзамість TLнаведеної таблиці, наприклад.

введіть тут опис зображення

НЕЗАЛЕГОВАНИЙ КОД

це код rev 1, який менш затуманений, ніж rev 2.

Це працюватиме на GCC / Linux. Я включив у коментарі додатковий код, необхідний для його запуску на Visual studio / Windows. Це велика різниця!

//Runs on gcc/linux. For visual studio / windows, change printf(...) 
//to printf(" %c%c%c",9*(i%4==1),i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),10*!(i%2)) and uncomment the following lines
//#include"stdafx.h"
//#include<stdlib.h>
//#include<time.h>
//#pragma warning(once:996;once:430) //allow the use of scanf instead of scanf_s, allow default type=int. 
//Though rather than using the pragma, it is shorter to follow compiler recommendation and use scanf_s and int.

#define u(t,s,c) if(t){puts(s);c;}  //if(test){puts(string);additional code;}

i,     //player input, loop counter
d,e,   //current and proposed direction
a,b;   //bit flags for where wumpus smelt / breeze felt

main(){
    srand(time(0));
    char q,p=19,h=rand()%p,w=rand()%p,  //Initialise player, hole and wumpus. q stores proposed player position.
    *m="e@LwQMQOSOLT-\\/\n \f ";        //Chars 0-11: movetable. Chars 12-14:symbol for player. Chars 15-18: graphics format.   

    while(p-h&&p-w){

        // Print warnings
        for(i=3;i--;){q=(p+m[p%4*3+i])%20;u(q==w,"you smell a wumpus",a|=2<<p)u(q==h,"you feel a breeze",b|=1<<p)}

        // graphic display 
        for(i=20;i--;)printf("%c%c",i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),m[i%4+15]);

        // Get player input and work out direction and room 
        scanf("%d",&i);
        e=(d+i/10)*m[p%4]%3;
        q=(p+m[p%4*3+e])%20;

        // i%5 is false if player inputs 5 (move player) otherwise true (shoot arrow) 
        if(i%5)
        {u(q-w,"arrow missed",w=(w+m[w%4*3+rand()%3])%20;a=0)else u(1,"YOU KILLED THE WUMPUS!",h=p)}
        else{p=q;d=e;u(p==h,"YOU FELL IN A HOLE!",)}
        u(p==w,"THE WUMPUS GOT YOU!",)
    }

}

ПИТАННЯ ТА КУРІОЗИТИ

Я скористався моментом, згаданим @professorfish, якщо вімп і яма починаються у випадкових місцях, гравець не повинен починати у випадковому місці. Гравець завжди починається в кімнаті 19, яка виходить на північ.

Я розумію, що коли вумпу «не впливає яма», то вімп може запускатися, або входити в приміщення, де знаходиться яма. Взагалі це спрощує речі за винятком одного пункту. У мене немає конкретної змінної, яка б свідчила про те, що гра закінчена; це закінчилося, коли гравець збігається з вовком або ямою. Тож, коли гравець перемагає, я показую повідомлення про перемогу, але переміщу яму до гравця, щоб вирватися з циклу! Я не можу поставити гравця в яму, оскільки там може бути вімп, і я отримаю повідомлення про wumpus, яке я не хочу.

Програма rev0 прекрасно працювала у візуальній студії, але IDE сказав, що "стек пошкоджений навколо змінної i" при виході. Це тому, що scanf намагається ввести intв себе char.повідомлення про те, що Денніс повідомив про неправильну поведінку на своїй машині Linux через це. У будь-якому випадку це фіксується за допомогою правильного типу в rev 1.

Лінія відображення плати в rev 0 незграбна і на інших платформах виглядає дещо іншою. У printf(" %c%c%c")середині% c відображається символ для друку. Останній% c - це або ASCII 0, або ASCII 10 (\ n, новий рядок із поверненням каретки в Windows.) У Windows, що працює в консолі, немає жодного символу, який піде вниз по лінії, не даючи повернення каретки. Якби я був, мені не знадобився б перший c% (ASCII 0 або ASCII 9 вкладка перед символом широти 1. Вкладки, як відомо, не визначено у своїй поведінці.) Провідний простір покращує форматування (ставить ширину 3 та 2 символи ближче до широти 1 символ .) У Rev 1 є перегляд цього рядка, який використовує символ \ f formfeed і тому не потребує символу формату на початку printf. Це робить його коротшим, але \ f не працює в Windows.


1
Я люблю запис.
Майкл Стерн

Я не впевнений , якщо це з - за змін , я повинен був зробити , щоб скомпілювати його з GCC на Linux (видалити перший включають, замінити scanf_sз scanfі включають в себе , stdio.hякщо я складаю , як оцінювач C ++ , ніж C), але це не зовсім працює для я. Наприклад, якщо я йду ліворуч, то повертаюся праворуч на початку ( 15 35), я перебуваю в іншій кімнаті, ніж у тій, з якої я почав.
Денніс,

@Dennis Я відстежив джерело помилки при виході. це scanf_s (нібито безпечний!), який "пошкоджує стек навколо змінної i", коли він намагається поставити те, що я вважаю, це 32-бітове ціле число в char. Отже, перше, що я б запропонував, - це перевірити тип, який scanf використовує для "% d", і змінити змінну i на цей тип. Я отримую правильну відповідь w / помилка виходу для char, правильну відповідь без помилки виходу для int та неправильну відповідь із типом Microsoft __int64 (еквівалентно довго, якщо я не ставлю "% lld"). версію, що не має волі, і у вас виникли проблеми з дисплеєм?
Рівень річки Св.

@steveverrill: Так, я спробував обидві версії. Це iсправді проблема. На сторінці чоловіка написано: " d Відповідає необов'язково підписаному десятковим цілим числом; наступний покажчик повинен бути вказівником на int ." Зміна типу змушує його працювати добре.
Денніс

@steveverrill: Я не знаю, як VS обробляє речі, але якщо ви компілюєте з GCC (як C, а не C ++), ви можете зберегти багато символів. Жодна з включень не потрібна, якщо ви замінюєте NULLна 0і scanf_sна scanf, вам не потрібно intраніше, mainі ви можете переміщатись iі dза межі основного (вони за замовчуванням до intініціалізовані 0). Крім того , ви можете визначити p=19,h=rand()%p,w=rand()%p, замінити m[]з , *mі це повинно бути можливо визначити макрос для всіх екземплярів if(...==...)puts(...);.
Денніс

9

GolfScript, 269 символів

{puts}:|;20,{;9{rand}:r~}$3<(:>"B:%d`w85>2n+Fup`y/>@D-=J7ldnx/W5XsLAb8~"{32-}%"`\24"{base}/3/{[.[~@].[~@]]}%:A=3r=0=:F;~:W;:P;{>A={0=F=}?:^P&!!{"You feel a breeze"|}*^W&!!{"You smell a wumpus"|}*'"#{'9.?r';STDIN.gets()}"'++~);(3%^=\4`={W={"Your arrow hit the wumpus"|0}{"Your arrow didn't hit anything"|W A=0=3r=:W>=.!\{"The wumpus catches you"|}*}if}{>:F;:>W=.!\{"You ran into the wumpus"|}*>P=.!\{"You fell into the pit"|}*&}if}do

Зауважте, що 163 відняли з числа символів для жорстко закодованих рядків. Якщо ви хочете налагоджувати вихід із зазначенням номерів кімнати, додайте наступний рядок відразу після першого появи ^:

'  YOU 'F'->'>+++puts'  DIRECTIONS [BRL] '^`+puts'  PIT 'P+puts'  WUMPUS 'W+puts 

Приклад сеансу (з додатковим виходом налагодження):

  YOU 6->11
  DIRECTIONS [BRL] [6 7 16]
  PIT 7
  WUMPUS 5
You feel a breeze
25
  YOU 11->16
  DIRECTIONS [BRL] [11 17 15]
  PIT 7
  WUMPUS 5
35
  YOU 16->11
  DIRECTIONS [BRL] [16 6 7]
  PIT 7
  WUMPUS 5
You feel a breeze
15
  YOU 11->6
  DIRECTIONS [BRL] [11 10 1]
  PIT 7
  WUMPUS 5
15
  YOU 6->10
  DIRECTIONS [BRL] [6 15 5]
  PIT 7
  WUMPUS 5
You smell a wumpus
14
Your arrow didn't hit anything
  YOU 6->10
  DIRECTIONS [BRL] [6 15 5]
  PIT 7
  WUMPUS 0
25
  YOU 10->5
  DIRECTIONS [BRL] [10 14 0]
  PIT 7
  WUMPUS 0
You smell a wumpus
24
Your arrow hit the wumpus

Ось перший робочий код. Повернувшись пізніше, щоб побачити більше гольфу.
Говард

Наразі мій код на 1 символ довший. Я шукаю будь-який можливий шлях до гольфу далі!
Тімтех

Не те, що вам потрібна моя допомога, але ви можете зберегти 14 символів, визначивши {puts}:|;, 5 символів замінивши Rта Wз ( -і >дозволяє усунути навколишні простори) та 9 символів шляхом випадання '> 'print(здається, це не вимагає питання).
Денніс

@Dennis Дякую Я обов'язково втілю деякі ваші пропозиції.
Говард

9

JavaScript (ECMAScript 6) - 2197 1759 -45% = 967,45 символів

Майже закінчений гольф ...

Включає графічний інтерфейс з картою ікосаедра та мега-кажанами для отримання повних бонусів.

Графічний інтерфейс Wumpus

  • У кожній кімнаті 4 кнопки: X(Яма); B(Мега-Бат); W(Wumpus); і P(Ти).
  • Ваше поточне місцезнаходження пофарбоване у синій колір.
  • Кнопки пофарбовані в червоний колір, якщо об'єкт, який він представляє, можуть бути в цьому місці, і зелений, якщо він точно не знаходиться в цьому місці.
  • WІ Pкнопки можна натискати тільки в кімнатах , прилеглих до вашого поточного розташування.
  • Якщо ви перемагаєте, фон стає зеленим, а якщо ви помираєте, фон стає червоним.

Код:

P=x=>parseInt(x,36);Q=(y,a=4)=>[P(x)<<a for(x of y)];e=Q("def45c6di7ej1ai1bj2af3bf9dg8eh46b57a1gh0280390678ci9cj24g35h",0);X=Q("o6fl6afnik27bloscfaf");Y=Q("icp8i8t4jej4encjjan6");A='appendChild';C='createElement';W='width';H='height';G='background-color';L='disabled';I='innerHTML';N='className';D=document;R=Math.random;B=D.body;E=[];F=1<0;T=!F;Z="XBWP";s=D[C]('style');s.type='text/css';t='.A{position:absolute;left:25px;top:25px}.D{'+W+':50px;'+H+':50px}.D button{'+W+':25px;'+H+':25px;float:left}.R{'+G+':red}.G{'+G+':green}.B{'+G+':blue}';for(i in X)t+='#D'+i+'{left:'+X[i]+'px;top:'+Y[i]+'px}';s[A](D.createTextNode(t));D.head[A](s);c=D[C]('canvas');c[N]='A';c[W]=c[H]=500;B[A](c);x=c.getContext('2d');x.beginPath();d=(i,b,v)=>{for(j=0;j<3;j++){E[e[3*i+j]][b][L]=v}};a=(i,l,v)=>{t=F;for(j=0;j<3;j++)t=e[3*i+j]==l?T:t;if(t)M[v]++;b=E[i][v];b.c=-1;for(j=0;j<3;j++)E[e[3*i+j]][v].c+=t?1:-1;for(j of E)j[v][N]=j[v].c==M[v]?'R':'G';};M=[0,0,0];S=v=>{M[v]=0;for(i of E){i[v][N]='';i[v].c=0}};for(i in X){for(j=3*i;j<3*i+3;j++)x.moveTo(X[i],Y[i])|x.lineTo(X[e[j]],Y[e[j]]);B[A](v=D[C]('div'));v[N]='A D';v.id='D'+i;E[i]=[];for(j in Z){b=E[i][j]=v[A](D[C]('button'));b[L]=T;b.i=i;b.c=0;b[I]=Z[j];}E[i][4][O='onclick']=function(){d(P,2,T);d(P,3,T);if(this.i==W)c[N]+=' G';else{S(2);W=e[3*W+R()*3|0];if(W==P)c[N]+=' R';else{a(P,W,2);d(P,2,F);d(P,3,F)}}};E[i][3][O]=function(){d(P,2,T);d(P,3,T);E[P][3][N]='';P=this.i;if(W==P||Q==P){c[N]+=' R';return}else if(Z==P){j=P;do{P=R()*20|0}while(P==W||P==Q||P==j);do{Z=R()*20|0}while(Z==j||Z==P);S(1)}d(P,2,F);d(P,3,F);E[P][3][N]='B';a(P,Q,0);a(P,Z,1);a(P,W,2)}}x.stroke();P=R()*20|0;do{W=R()*20|0}while(W==P);do{Q=R()*20|0}while(Q==P);do{Z=R()*20|0}while(Z==P);E[P][3][N]='B';a(P,Q,0);a(P,Z,1);a(P,W,2);d(P,2,F);d(P,3,F)

Ви отримуєте 1066 без ECMA 6, використовуючи компілятор закриття.
AMK

Мені було цікаво, як набагато легше буде, коли у вас є графічне зображення, яке допоможе визначити, де є речі. 1+, але це занадто просто :)
Sylwester

9

Bash, 365 (перша робоча версія 726!)

ЗГІБУВАННЯ ГОЛЬФСКРИПТ?

@Dennis в основному зробив для мене все гольф. Спасибі!

Програма передбачає дійсне введення. Дійсне введення - це напрямок, який ви обираєте (1 праворуч, 2 ліворуч, 3 задньою стороною), за яким слід діяти (4 стріляти, 5 ходити).

Деякі пояснення

Я зазвичай роблю великі багатослівні пояснення, але це, мабуть, занадто складно, щоб мене турбували.

Кожна вершина на графіку додекаедра кодується у вигляді літери (a = 1, b = 2, ... t = 20).

Вихідна позиція гравця завжди 20 (а вони стоять спиною до 18), оскільки це не має значення саме по собі, мають значення лише відносні позиції гравця, піт і вімп.

Змінна $pзберігає розташування гравця. $rзберігає попереднє місце програвача. $w- вим’я і $h(Н для отвору) - яма.

Код

p=t
r=r
j=echo
Z=npoemfsgnohtksblbtpckdpljqnriogelfhkbqrcaiadjhagimsmjtqecrdf
q(){ $j ${Z:RANDOM%19*3:1};}
C(){ [[ ${!1} =~ ${!2} ]];}
d(){ s=${Z:30#$1*3-30:3};}
w=`q`
h=`q`
for((;;));{
b=$p
d $p
u=u${s#*$r}$s
C w p&&$j The wumpus ate you&&exit
C h p&&$j You fell in the pit&&exit
C u w&&$j You smell the wumpus
C u h&&$j You feel a breeze from a pit
read i
F=5
y=${u:i/10:1};C i F&&p=$y&&r=$b||{ d $w;C y w&&$j You killed the wumpus&&exit;$j You missed;w=${s:RANDOM%3:1};};}

Історія версій

  1. Початковий реліз, 698 символів
  2. Виправлена ​​помилка, де "Ви відчуваєте вітер" та "Ви відчуваєте запах вовну" не можуть відображатися одночасно; зберегли 39 символів, зробивши функцію генерації випадкових чисел функцією.
  3. Згадав, що вімп рухається, якщо ти стріляєш і промахнешся. 726 символів.
  4. Зробили grep -oEзмінну. Збережено 5 символів.
  5. Зробили [a-z]{3}змінну. Збережено 3 символи.
  6. Зробили echoзмінну. Збережено 5 символів.
  7. Діяв на більшість пропозицій @Dennis. Збережено 72 символи.
  8. Додано всі пропозиції, що залишилися Збережено 68 символів.
  9. Збережено 2 знаки від пропозиції @DigitalTrauma.
  10. Виправлена ​​основна помилка, куди ви можете стріляти в віпс, лише якщо справа. Кількість символів однакова.
  11. Використовується розширення параметрів для гоління 2 символів за допомогою $m.
  12. Поголили багато сну, скидаючись grepі трохи чутливіше.
  13. Визначається Cяк функція пошуку regexp, яка використовується в операторах if, а Eтакож як функція друку "Ви вбили wumpus" та виходу.
  14. Збережено 1 char за перестановкою "if statement".
  15. Зберегли багато символів, позбувшись d, і зняли непотрібні дужки.
  16. Виправлені помилки. Додано багато символів :(
  17. ЗБЕРЕЖЕННЯ МОРУ ( http://xkcd.com/1296/ )
  18. Ще одна з ідей @Dennis (економія кількох символів), і моє хитро (ab) використання непрямості (економія 1 char).
  19. Виправлення стилю для q ().
  20. знову додано належний вихід

Вибірка зразка

"In:" - це вхід, "Out: є вихід".

Гравець трохи блукає, понюхає ворс і стріляє. Вони сумують, а в їхній кімнаті заходить заплідник і їсть їх.

В: 15

В: 15

В: 25

В: 25

В: 15

Вихід: Ви відчуваєте запах вум

В: 14

Вихід: Ви пропустили

Назовні: з'їв тебе кабачок


1
Я думаю, ви можете зробити свій код принаймні на 100 байт коротшим. 1. exitлише на один байт довше, g=1і це виключає необхідність тестування на ненульові gта деякі elifоператори. 2. Ви можете використовувати ((i==35))замість [ $i = 35 ]і ...&&...замість if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}і n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}обидва збережіть кілька байтів.
Денніс

1
Замініть while :;do... doneна for((;;);{... }для економії на 3 чари
Digital Trauma

1
@professorfish: Я думаю, що функція працювала б краще, ніж поточний підхід до вирізання рядків. Наприклад, d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}дозволить замінити означення sі nна d $pі на d $w. Якщо , крім того , визначити u=${s#*$r}$s(і коригувати визначення lі fвідповідно), вам не потрібно буде $kі $mбільше. Економить 83 байти, я думаю. Також місця в ньому q ()не потрібно.
Денніс

1
@professorfish: І ви можете зберегти 3 додаткові байти, визначивши c(){ [[ $1 =~ $2 ]];}та замінивши, наприклад, другий останній рядок на c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Денніс

1
@professorfish: Використання запропонованої нами функції має бути на 3 байти коротше. Ви можете заощадити 106 додаткових байтів, замінивши чотири рядки після того, як b=$pз d $p;u=u${s#*$r}$s, лінії після read iз y=${u:i/10:1};C $i 5&&{ p=$y;r=$b;}||{ d $w;C $y $w&&$j You killed the wumpus&&exit;$j You missed;w=${s:RANDOM%2:1};}і позбавленні від E().
Денніс

6

GolfScript ( 206 198)

[5:C,]{{.{[~@]}:>~.{-1%}:<~}%.&}8*({[.<><.<><]}:F~-{99rand}$~5,{.<{>.'You smell a wumpus.\n'4{$F@?~!!*}:Q~{print}:,~}3*{>.'You feel a breeze.\n'5Q,}3*'"#{'C):C';STDIN.gets()}"'++~~:&9/{>}*&5%{'You killed the wumpus.'3Q{\<{>}3rand*\"Your arrow didn't hit anything.\n",0}or}{\;.'You fell into the pit.'4Q}if\.'You were killed by the wumpus.'4Q@or:n!}do];

Нарешті наздогнав версію таблиці пошуку Денніса, з якої позичає зовсім небагато. Цікава річ у цій версії, що вона не має таблиці пошуку для планування кімнати.

У 60 обертальні симетрії ікосаедра ізоморфні знакозмінної групи по 5 букв, A_5. Спробувавши всілякі підходи до компактного представлення групи, я повернувся до найпростішого: кожен елемент є перестановкою рівномірного паритету. Групу можна створити з двох генераторів більш ніж одним способом: підхід, який я використовую, використовує генератори 3та 3 1. Вони дозволяють нам створювати 1 = 3 3 1, 2 = 3 3 1 3 1і 3 = 3.

Зауважте, що напрямок 3відповідає елементу порядку 2, тому що, пройшовши через двері за вами, ці двері знову за вами. Напрямок 1відповідає елементу порядку 5, обходячи вершину ікосаедра. (Аналогічно елемент 2). І комбінація 3 1порядку 3, оскільки вона об'їжджає кімнати, прилеглі до тієї, що починається позаду вас.

Таким чином, ми шукаємо перестановку порядку 2 для подання напрямку 3та перестановку порядку 5 для подання напрямку, 1такого 3 1як для порядку 3.

У A_5 є 15 перестановок порядку 2, і для кожної є 8 перестановок для кандидата для 1(і, отже, для 3 1). Там очевидне тяжіння до [4 3 2 1 0]для 3: реверсивний масив просто -1%. З можливих супутніх перестановок 3 1я вибрав [0 1 3 4 2], що передбачає досить коротку реалізацію як [~@].

Безумовно

# Generate the 60 permutations by repeated application of `3 1` and `3`
[5:C,]{{.{[~@]}:>~.{-1%}:<~}%.&}8*
# Remove [0 1 2 3 4] and its equivalence class (apply 3 (3 1)^n 3 for n in 0,1,2)
({[.<><.<><]}:F~-
# Shuffle the remaining 57 options to select random starting points for wumpus and pit
# Note that this introduces a slight bias against them being in the same room,
# but it's still possible.
{99rand}$~
# Start player at [0 1 2 3 4]
5,
{
    # Stack: Pit Wumpus Player
    .<
    # The adjacent rooms to the player are Player<> , Player<>> , and Player<>>>
    # If the wumpus is in one of those rooms, say so.
    {
        >."You smell a wumpus.\n"4
        {
            # ... X str off
            $F@?~!!*
            # ... str off$F X ?~!! *
            # Which means that we leave either str (if off$ and X are equivalent)
            # or the empty string on the stack
        }:Q~
        {print}:,~
    }3*
    # Ditto for the pit
    {>."You feel a breeze.\n"5Q,}3*
    # Read one line from STDIN.
    '"#{'C):C';STDIN.gets()}"'++~~
    # Stack: Pit Wumpus Player Player< Input
    # Find the room corresponding to the specified direction.
    :&9/{>}*&
    # Stack: Pit Wumpus Player TargetRoom Input
    5%{
        # Shoots:
        "You killed the wumpus."3Q
        {
            \<{>}3rand*\ # Move the wumpus to an adjacent room
            "Your arrow didn't hit anything.\n", # Inform
            0 # Continue
        }
        or
    }{
        # Moves:
        \;
        # If player and pit share a room, say so.
        ."You fell into the pit."4Q
    }if
    # If player and wumpus share a room, say so.
    # NB If the player walks into a room with the pit and the wumpus,
    # the `or` favours the pit death.
    \."You were killed by the wumpus."4Q@or
    # Save the topmost element of the stack for output if we break the loop. Loop if it's falsy.
    :n!
}do
# Ditch the junk.
];

Приємний алгебраїчний підхід! Хоча є незначна помилка: 10/@3%=намагається отримати доступ до четвертого елемента масиву довжиною 3, якщо вхід є 35.
Денніс

@Dennis, так, я зрозумів після того, як лягав спати. Я можу придумати різні способи виправити це, все коштувало 2.
Пітер Тейлор

Ви можете повернути один шар 9/3%@3%=.
Денніс

Наразі я перебуваю на 7 років із більш різкою перебудовою. Але той 1 знак 9/замість 10/все ще працює, тож спасибі.
Пітер Тейлор

5

Wumpus , 384 - 129 (рядки) = 255 байт

1SDL2vSD70L?.;;3AL1a!?,9)".supmuw a llems uoY"99+1.
II5x?,&WC2.           L2a!?,9)".ezeerb a leef uoY"93*2.
L1a!,FCFC[&WCL1a!?,"!supm",AW#16#[(=]?.;;l(&o1.
    ?,".uoy eta ",".gnih","uw eht dellik uoY"#22&oN@
     #15#L2a!?. ,"supmu","tyna tih t'ndid worra ruoY"#31&oND";"4L1a!?.;;L1xSUL1xSD=F-#81~4~?.;;;CCWC=F-#97~4~?.;;;2.
 ,"nto the pit."|       "w ehT"l&oN@
 |"i llef uoY"l2-&oN@

Спробуйте в Інтернеті!(Звичайно, TIO не має великого сенсу, тому що ви не можете інтерактивно користуватися програмою там, і коли програма закінчиться інструкціями на STDIN, вона прочитає 0 0, що еквівалентно 3 4, тож ви закінчите стріляйте стрілками, поки Wumpus не рухається туди або вас не вбиває.)

Запускаючи це локально, переконайтеся, що передача рядків після другого номера кожного входу розмивається (адже Wumpus потребує цього, щоб визначити, що число закінчилось). У Powershell мені якось потрібно ввести ще одного символу після передачі рядків, щоб він працював (не важливо, який символ, але я просто використовував подвійні рядкові канали для тестування).

Тут ще багато місця для гри в гольф, але спроба абсолютно нових макетів потребує певного часу. Підсумкова оцінка також багато в чому залежить від фактичних рядків, які я використовую, тому що в 2D мові рядок з N байтів, як правило, коштує вам дорожче ніж N байт вихідного коду, тому що це ставить значні обмеження в макеті коду, і ви часто потрібно розділити його на кілька розділів (утримуючи додаткові подвійні лапки). Зрештою, якби я зменшив кожну рядок до однієї літери (і від -129 до -12), я, певно, заощадив би тону байтів.

Пояснення

По-перше, відмова від відповідальності: незважаючи на назву мови, вона не була розроблена для того, щоб зробити полювання на Wumpus особливо легким. Натомість я спершу розробив мову навколо теми трикутників, закінчив структуру даних ікосаедра і вирішив назвати її Wumpus через це.

Так що, хоча Wumpus в основному складається на стеках, він також має 20 регістрів, які розташовані навколо граней ікосаедра. Це означає, що ми отримуємо структуру даних, щоб представити карту безкоштовно. Єдине, що ми не можемо легко зробити, - це знайти конкретні обличчя на ікосаедрі, тому для їх пошуку нам потрібно «розгорнути d20», поки не опинимось на обличчі, яке ми шукаємо. (Це можна зробити детермінованим способом, але це займе набагато більше байтів.) Пошук таких осіб, як це, закінчується майже напевно (тобто з вірогідністю 1), тому пошук, який ведеться вічно, не викликає проблем на практиці).

Вищевказаний код є гольф-версією цієї першої реалізації з безпечнішим макетом:

1SDL2vSD70L?.;;2.  < Setup; jumps to third line which starts the main loop

3AL1a! ?,".supmuw a llems uoY"#19&oN03.          < This section checks the player's surroundings.
        L2a!?,".ezeerb a leef uoY"#18&oN04.
             AW(=12[?.;;7.

    }&WC#11.                                     < This section reads the input. The top branch moves, the bottom branch shoots
II5x^                                              and kills or moves the wumpus.
     {FCFC[&WCL1a !?,"!supmuw eht dellik uoY"#22&oN@
                    ".gnihtyna tih t'ndid worra ruoY"#31&oND#59 9L1a!?.;;L1xSUL1xSD=F-#82~9~?.;;;CCWC=F-#98~9~?.;;;#11.

L1a!?,".uoy eta supmuw ehT"#19&oN@               < This section checks whether the player dies.
     L2a!?,".tip eht otni llef uoY"#22&oN@         Otherwise, we return back to the third line.
          2.

Оскільки гольф в основному передбачає стиснення макета, я зараз лише поясню цю версію (поки я не додаду жодних хитрощів, що виходять за рамки реструктуризації коду).

Почнемо з коду настройки:

1SDL2vSD70L?.;;2.

Спочатку всі обличчя встановлюються на 0 . Ми будемо кодувати wumpus, встановивши 1 біт відповідної грані, а ям, встановивши 2-бітний. Таким чином, вони можуть бути в одній кімнаті. Положення гравця взагалі не буде записано на ікосаедрі, натомість воно завжди буде активним обличчям (одночасно активний лише один з 20 реєстрів).

1S     Store a 1 in the initially active face to put the wumpus there.
D      Roll the d20. Applies a uniformly random rotation to the icosahedron.
L2vS   Load the value of that face (in case it's the wumpus's), set the 2-bit
       and store the result back on that face.

Тепер нам потрібно знайти випадкове порожнє обличчя, щоб помістити гравця.

D      Roll the D20.
70     Push 7 and 0 which are the coordinates of the D in the program.
L      Load the value of the current face.
?.     If that value is non-zero (i.e. the active face has either the
       wumpus or the pit), jump back to the D to reroll the die.
;;2.   Otherwise, discard the 0 and the 7 and jump to (0, 2), which is
       the beginning of the main loop.

Цей наступний розділ перевіряє оточення гравця та друкує відповідні попередження:

3AL1a! ?,".supmuw a llems uoY"#19&oN03.
        L2a!?,".ezeerb a leef uoY"#18&oN04.
             AW(=12[?.;;7.

Це цикл, який ми проходимо 3 рази. Кожен раз, коли ми дивимось на правого сусіда, друкуємо відповідний рядок (-ів), якщо є небезпека, а потім обертаємо ікосаедр на 120 °.

3    Push a 3 as a loop counter.
A    Tip the icosahedron onto the NW neighbour of the active face, which
     will be used to represent the right-hand room.
L1a  Extract the 1-bit of the value on that face.
!?,  If that value is zero, strafe to the next line, otherwise continue.

  ".supmuw a llems uoY"#19&oN03.
     Print "You smell a wumpus.", a linefeed and then jump to the next line.

L2a  Extract the 2-bit of the value on that face.
!?,  If that value is zero, strafe to the next line, otherwise continue.

  ".ezeerb a leef uoY"#18&oN04.
     Print "You feel a breeze.", a linefeed and then jump to the next line.
A    Tip back to the original active room (where the player is).
W    Rotate the icosahedron by 120°, so that the next iteration checks
     another neighbour.
(=   Decrement the loop counter and duplicate it.
12   Push 1, 2, the coordinates of the cell after the 3 (the loop counter).
[    Pull up one copy of the loop counter.
?.   If it's non-zero, jump to the beginning of the loop, otherwise continue.
;;7. Discard the 2 and the 1 and jump to (0, 7), which reads the player's
     input for this turn.

У наступному розділі зчитується два гравця з гравця, а потім або рухається гравець, або стріляє стрілка. Перший - тривіальний, другий - менш. Основним питанням для стрільби є випадок, коли вона пропускається. У такому випадку нам а) потрібно йти шукати wumpus, щоб перемістити його, а потім b) повернутися до кімнати гравця та правильної орієнтації ікосаедра (щоб «назад» залишався «назад»). Це найдорожча частина всієї програми.

    }&WC#11.
II5x^
     {FCFC[&WCL1a !?,"!supmuw eht dellik uoY"#22&oN@
                    ".gnihtyna tih t'ndid worra ruoY"#31&oND#59 9L1a!?.;;L1xSUL1xSD=F-#82~9~?.;;;CCWC=F-#98~9~?.;;;#11.

Точка входу до цього розділу - Iліворуч.

II   Read the integers from STDIN.
5x   XOR the second one with 5.
^    Turn either left or right, depending on the previous result. If the
     second input is 4, XORing with 5 gives 1 and the IP turns right.
     Otherwise, we get 0 and the IP turns left.

If the player entered 5, move:

}    Turn right so that the IP moves east again.
&W   If the room indicator is X, rotate the icosahedron by X*120°. This
     puts the target room south of the active face (where the back room
     normally is).
C    Tip the icosahedron onto the southern face. This moves the player there.
     Due to the way tipping works, the formerly active face will now be
     the southern neighbour, i.e. correctly at the back of the player.
#11. Jump to (0, 11), the final section which checks whether the player
     stepped into the pit or onto the wumpus.

If the player entered 4, move:

{    Turn left so that the IP moves east again.
F    Store the active face index (the player's position) on the stack.
CFC  Also store the face index of the southern neighbour (the back room)
     on the stack, so that we can recover the correct orientation if
     we need to.
[    Pull up the player's room choice.
&WC  Tip the icosahedron onto the corresponding face (same as for the move action)
L1a  Extract the 1-bit of the value on that face to check whether the arrow
     hit the wumpus.
!?,  If that value is zero, strafe to the next line, otherwise continue.

  "!supmuw eht dellik uoY"#22&oN@
     Print "You killed the wumpus.", a linefeed, and terminate the program.

".gnihtyna tih t'ndid worra ruoY"#31&oN
     Print "Your arrow didn't hit anything." and a linefeed.

This next bit is a loop which searches for the wumpus:

D    Roll the d20. The easiest way to search for the wumpus is to look at
     random faces.
#59 9
     Push 59 and 9, the coordinates of the beginning of this loop.
L1a  Extract the 1-bit of the value on the current face.
!?.  If that value is zero, jump back to the beginning of this loop to
     try another face, otherwise continue.
;;   Discard the 9 and the 59.
L1xS Unset the 1-bit of the current face to remove the wumpus there.
U    Tip the icosahedron onto a random neighbouring face. This moves us
     to a random adjacent room.
L1xS Set the 1-bit of the current face to put the wumpus there.

This next bit contains two loops which get us back to the player's room
with the correct orientation. We do this by first searching for the room
at the player's back, and then looking through its neighbours to find the
player's room.

D    Roll the d20.
=F-  Duplicate the back room index and subtract the current face index.
#82~9~
     Push 82 and 9 and pull up the difference we just computed.
?.   If the difference is non-zero (we've got the wrong room), jump back
     to the D to try again. Otherwise continue.
;;;  We've found the back room. Discard the 9, the 82 and the back room index.
C    Tip the icosahedron onto the southern face (one of the candidate
     neighbours which might be the player's room).
CWC  This begins the loop that searches for the player's room. Tip onto
     the back room, rotate by 120°, tip back. This cycles through the
     neighbours of the back room, while keeping the active face on those
     neighbours.
=F-  Duplicate the player's room index and subtract the current face index.
#98~9~
     Push 98 and 9 and pull up the difference we just computed.
?.   If the difference is non-zero (we've got the wrong room), jump back
     to the CWC to try again. Otherwise continue.
;;;  We've found the player's room and since we entered from the back room
     via C, we've also got the correct orientation. Discard the 9, the 98
     and the player's room index.
#11. Jump to (0, 11), the final section which checks whether the player
     stepped into the pit or onto the wumpus.

Фу, це була важка частина. Тепер нам просто потрібно перевірити, чи вмирає гравець, інакше почнеться через основний цикл:

L1a!?,".uoy eta supmuw ehT"#19&oN@
     L2a!?,".tip eht otni llef uoY"#22&oN@
          2.

Структура цього розділу по суті ідентична структурі, яку ми використовували під час перевірки оточення гравця: ми перевіряємо 1 біт поточного обличчя (приміщення гравця), і якщо він встановлений, ми друкуємо The wumpus ate you.та припиняємо програму. В іншому випадку ми перевіряємо 2-розрядну You fell into the pit.версію програми і друкуємо та припиняємо програму. В іншому випадку ми доходимо до того, 2.що стрибає назад до початку основної петлі (за координатами (0, 2)).


1

awk - великий

Це не вийшло так коротко, як я сподівався, але я застосував дещо інший підхід до роботи з графіком, тому я все-таки розміщую версію, яка не перебуває в кольці.

Я скористався тим, що ікосаедр (20-сторонній багатогранник) при обертанні, що зберігає орієнтацію, ізоморфний змінній групі 5 ступеня (5 перестановок елементів, що мають парне число циклів парної довжини). Потім я вибираю дві перестановки довжиною циклу 5 як "ліворуч" та "праворуч", і вибираю одну перестановку довжиною циклу 2 як "назад". Використовуючи ці дані, я будую графік з однієї кімнати, проходячи гамільтонів шлях (2xRRRLLLRLRL, використовуючи 3xRB в кожній кімнаті для зйомки 3 можливих напрямків).

function meta(z,a,b,c,d) {
    if(z==COMPOSE) {
        split(a,c,"");
        split(b,d,"");
        return c[d[1]]c[d[2]]c[d[3]]c[d[4]]c[d[5]];
    }
    if(z==WALK) {
        split(R" "R" "R" "L" "L" "L" "R" "L" "R" "L,c);
        for(b = 1; b <= 20; b++) {
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            a = meta(COMPOSE, a, c[b % 10 + 1]);
        }
    }
    if(z==TEST) {
        a = map[meta(COMPOSE,U,L)];
        b = map[meta(COMPOSE,U,R)];
        c = map[meta(COMPOSE,U,B)];
        if(a==W||b==W||c==W) print "You smell the wumpus";
        if(a==P||b==P||c==P) print "You feel a breeze";
        if(map[U]==W) {
            print "You have been eaten by the wumpus";
            exit;
        }
        if(map[U]==P) {
            print "You have fallen into a bottomless pit";
            exit;
        }
    }
    if(z==ARROWTEST) {
        if(A==W) {
            print "You have slain the wumpus!";
            exit;
        } else {
            for(a in p) if(map[a]==W) break;
            W=map[meta(COMPOSE,a,v[int(rand()*3)+1])];
        }
    }
}

BEGIN {
    COMPOSE = 0;
    WALK = 1;
    TEST = 2;
    ARROWTEST = 3;
    L = 35214;
    R = 35421;
    B = 35142;
    split(R" "L" "B,V);
    meta(WALK,L);
    W = int(rand()*19)+2;
    P = int(rand()*19)+2;
    U = L;
    meta(TEST);
}

{
    d=int($0/10);
    m=$0%10;
    if(m==5) U = meta(COMPOSE,U,V[d]);
    else if(m==4) {
        A = map[meta(COMPOSE,U,V[d])];
        meta(ARROWTEST);
    }
    meta(TEST);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.