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