Охота на Wumpus


39

Когда я был мальчиком, дети ходили в компьютерные магазины и играли в «Охоту на вумпа», пока персонал не выгнал нас. Это была простая игра, программируемая на домашних компьютерах середины 1970-х годов, машины настолько зачаточные, что вместо микропроцессоров размером с микросхему, я думаю, у некоторых из них были настоящие микросхемы.

Давайте вспомним ту ушедшую эпоху, воспроизведя игру на современном оборудовании.

  1. Игрок начинает игру в случайной комнате на карте икосаэдра (таким образом, всего имеется 20 комнат, связанных друг с другом как лица икосаэдра, и каждая комната имеет ровно три выхода).

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

  3. Игрок несет лук и бесконечное количество стрел, которые он может в любой момент выстрелить в комнату перед собой. Если wumpus находится в этой комнате, он умирает, и игрок выигрывает. Если wumpus не было в этой комнате, он поражен и перемещается случайным образом в любую из трех комнат, связанных с его текущим местоположением.

  4. Одна случайно выбранная комната (гарантированно не та, в которой начинается игрок) содержит бездонную яму. Если игрок находится в какой-либо комнате рядом с ямой, он чувствует легкий ветерок, но не понимает, из какой двери дул легкий ветерок. Если он входит в комнату с ямой, он умирает, а Вумпус побеждает. На ямку яма не влияет.

  5. Если игрок входит в комнату Вумпуса или если Вумп входит в комнату игрока, Вумпус побеждает.

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

  7. В целях подсчета очков каждая игровая строка («Ты чувствуешь бриз», «Ты чувствуешь запах глыбы», «Твоя стрела ничего не попала» и т. Д.) Может считаться одним байтом. Не злоупотребляйте этим, чтобы скрыть код игры в тексте; это только для взаимодействия с игроком.

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

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

  10. Наименьшее количество байтов, как настроено, побеждает.

ОСНОВНОЙ исходный код для версии игры (не обязательно соответствующей вышеприведенным правилам и, в любом случае, совершенно безглобной) можно найти на этом веб-сайте и, возможно, других.


Некоторые пояснения: 3. если глыбы не было в этой комнате, они напуганы и перемещаются в одну из ТРИ комнаты ... так что если вы выстрелите стрелой и промахнетесь, вампус может прийти и убить вас, верно? И wumpus будет двигаться только в случае испуга, в противном случае он просто остается на месте? 6. Я понимаю, что направление игрока определяется комнатой, из которой он вышел. Так что, если он приедет с юга, его варианты будут 1. северо-восток 2. северо-запад 3. юг, и если он придет с севера, все будет наоборот. Кроме того, ваши правила кажутся более простыми / более подходящими для игры в гольф, чем справочная программа (которую я еще детально не исследовал). Я прав?
Уровень Река St

Argh! Я не могу найти никаких изображений двойного графика икосаэдра в сети.
Джек М,

1
@steveverrill Да, если ты напугал его, он может прийти и убить тебя. Если вы не напугали его, он не двигается. В игре много вариаций; например, многие версии позволяют стрелкам кружиться и убивать вас. Я позаботился об этом.
Майкл Стерн

3
@JackM карта граней икосаэдра идентична карте вершин додекаэдра, и этот граф легко найти. Попробуйте, например, wolframalpha.com/input/?i=DodecahedralGraph+edgerules или эквивалентную команду Mathematica GraphData ["DodecahedralGraph", "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. Портирован ответ professorfish в от Bash к 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*&>..., еще 1, вставляя 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 и отверстия. Это можно увидеть в истории редактирования.)

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");}

Еще раз спасибо Деннису за рефакторинг моего кода:

Символ константы 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), почувствовал запах wumpus (2) или оба (3). (Если игрок стреляет стрелкой и пропускает, переменная, aсодержащая информацию о положении wumpus, сбрасывается.)

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

ИКОСАЭДРОН ПРЕДСТАВИТЕЛЬСТВО

Примечание: этот раздел основан на версии 1

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

Игрок может быть ориентирован на 3 возможных оси, которые представлены следующими символами: север-юг, -северо-восток-юго-запад, \северо-запад-юго-восток /. В любой комнате у него есть ровно один выход на каждую из доступных ему осей. На показанном дисплее игрок делает полный цикл по часовой стрелке. Как правило, по маркеру игрока легко определить, откуда он приехал и, следовательно, куда ему разрешено идти.

Один случай, который немного труден для непосвященного глаза, является четвертым. Когда вы видите наклон в одном из этих полярных рядов, игрок вышел из полярной ячейки, ближайшей к внешнему концу наклона, и, как правило, обращен к экватору. Таким образом, игрок смотрит на юго-восток, и его варианты: 15 (юг, ячейка справа) 25 (север-восток, ячейка вверху) или 35 (север-запад, ячейка внизу).

Итак, в основном я сопоставляю икосаэдр с сеткой 5x4 с ячейками с номерами от 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!",)
    }

}

ВОПРОСЫ И ЦЕНЫ

Я воспользовался тем, что упомянул @professfishfish, если wumpus и яма начинаются в случайных местах, игроку не нужно начинать в случайном месте. Игрок всегда начинается в комнате 19 лицом к северу.

Я понимаю, что, поскольку яма "не подвержена влиянию ямы", она может начинаться или входить в комнату, где находится яма. В целом это упрощает вещи, за исключением одного момента. У меня нет конкретной переменной, чтобы указать, что игра окончена; это заканчивается, когда игрок совпадает с Wumpus или ямой. Поэтому, когда игрок выигрывает, я отображаю сообщение о победе, но перемещаю яму игроку, чтобы выбить из цикла! Я не могу поставить игрока в яму, потому что там может быть wumpus, и я получу сообщение о wumpus, которое мне не нужно.

Программа rev0program отлично работала в Visual Studio, но IDE сказала "стек поврежден вокруг переменной i" при выходе. Это происходит потому , что зсапЕ пытается Погружает 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 имеет ревизию этой строки, которая использует символ \ formfeed и поэтому не нуждается в символе форматирования в начале printf. Это делает его короче, но \ f не работает в Windows.


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

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

@Dennis Я выследил источник ошибки при выходе. это scanf_s (предположительно безопасный!), который «портит стек вокруг переменной i», когда он пытается поместить в символ 32-разрядное целое число. Поэтому первое, что я хотел бы предложить, это проверить тип, который scanf использует для «% d», и изменить переменную i на этот тип. Я получаю правильный ответ с ошибкой выхода для char, правильный ответ без ошибки выхода для int и неправильный ответ с типом Microsoft __int64 (эквивалентно long long, если я не введу «% lld».) версия ungolfed а у вас были проблемы с дисплеем?
Уровень Река St

@steveverrill: Да, я пробовал обе версии. Тип iдействительно проблема. Страница man говорит: « 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 символ длиннее. Я ищу любой возможный путь в гольф дальше!
Timtech

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

@ Денис Спасибо. Я обязательно выполню некоторые из ваших предложений.
Говард

9

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

Почти закончил игру в гольф ...

Включает графический интерфейс с картой икосаэдра и мега-летучих мышей для полных бонусов.

Wumpus GUI

  • Каждая комната имеет 4 кнопки: X(Яма); B(мега-летучая мышь); W(Вумпус); и 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

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

Догоняете с GOLFSCRIPT?

@Dennis в основном сделал все для меня в гольф. Благодарность!

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

Некоторое объяснение

Я обычно делаю большие подробные объяснения, но это, вероятно, слишком сложно для меня, чтобы меня беспокоить.

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

Стартовая позиция игрока всегда равна 20 (а они стоят спиной к 18), так как это само по себе не имеет значения, имеют значение только относительные позиции игрока, ямы и вумпуса.

Переменная $pхранит местоположение игрока. $rсохраняет предыдущее местоположение игрока. $wявляется wumpus и $h(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. Вспомнил, что wumpus движется, если вы стреляете и пропускаете. 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как функция поиска регулярных выражений для использования в операторах if, а также Eкак функция, печатающая «Ты убил глупца» и выходящая из нее.
  14. Сохранено 1 символ путем перестановки «если утверждение».
  15. Спасло много символов, избавившись от d , и убрал лишние скобки.
  16. Исправлены ошибки. Добавлено много символов :(
  17. MOARR SAVINGS ( http://xkcd.com/1296/ )
  18. Еще одна идея @Dennis (сохранение нескольких символов) и мое хитрое (ab) использование косвенного обращения (сохранение 1 символа).
  19. Исправление стиля для q ().
  20. повторно добавлен правильный вывод

Пробный прогон

«In:» является входом, «Out: является выходом».

Игрок немного бродит, нюхает гнойник и стреляет. Они скучают, и глыба входит в их комнату и ест их.

В: 15

В: 15

В: 25

В: 25

В: 15

Out: вы чувствуете запах Wumpus

В: 14

Out: вы пропустили

Out: wumpus съел тебя


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
@professfishfish: Я думаю, что функция будет работать лучше, чем нынешний подход с использованием метода grep-cut. Например,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
@professfishfish: И вы можете сохранить 3 дополнительных байта, определив c(){ [[ $1 =~ $2 ]];}и заменив, например, строку с последней на последнюю c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Деннис

1
@profestivalfish: Использование предложенной мной функции должно быть на 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.

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

Ungolfed

# 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.
Деннис

@ Денис, да, я поняла после того, как пошла спать. Я могу придумать различные способы исправить это, все стоит 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 мне как-то нужно ввести еще один символ после перевода строки, чтобы он работал (не имеет значения, какой символ, но я просто использовал двойные переводы строки для тестирования).

Существует много возможностей для игры в гольф, но попытка совершенно новых раскладок занимает некоторое время. Окончательная оценка также во многом зависит от фактических строк, которые я использую, потому что в двумерном языке строка из N байтов имеет тенденцию стоить вам больше, чем N байтов исходного кода, потому что это накладывает значительные ограничения на компоновку кода, и вы часто нужно разбить его на несколько разделов (влекут за собой дополнительные двойные кавычки). В крайнем случае, если бы я сократил каждую строку до одной буквы (и от -129 до -12), я бы, вероятно, сэкономил бы тонну байтов.

объяснение

Во-первых, отказ от ответственности: несмотря на название языка, он не был разработан, чтобы сделать реализацию Hunt the 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, чтобы переместить его, а затем б) вернуться в комнату игрока и установить правильную ориентацию икосаэдра (чтобы «назад» осталось «назад»). Это самая дорогая часть всей программы.

    }&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.