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. Этот компилятор не требует include
s в программе 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.