REV0 C ++ (Windows의 Visual Studio) 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 (Cygwin의 GCC), 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의 변경 사항은 다음과 같습니다.
Windows 용 Cygwin Linux 에뮬레이터에서 GCC 컴파일러를 추천 해 주신 @Dennis에게 대단히 감사합니다. 이 컴파일러는 include
rev 0 프로그램에서 s를 필요로하지 않으며 int
변수의 기본 유형을 허용 하며 main.
이것이 인생을 변화시키는 골프 팁입니다!
또한 Linux에서 실행한다는 \f
것은 캐리지 리턴을 수행하지 않고 커서가 아래로 이동 함을 의미합니다 (Windows와 달리 인쇄 가능한 기호 만 생성됨). 이는 보드를 인쇄하는 printf 문을 상당히 단축시킬 수있었습니다.
의견에서 Dennis의 몇 가지 추가 팁과 내 자신의 조언 중 하나 : 화살표가 부비를 쳤는지 확인할 때 상태 변경 : if(q==w)
> if(q-w)
(..else ..가 반대로 됨)
선수가 제련 장소 / 바람에 대해 알고있는 정보를 보여주는 그래픽 디스플레이가 추가되어 35 %의 보너스를 요구합니다. (나는 wumpus와 구멍의 정확한 위치를 보여주는 이것의 오래된 디버그 버전을 삭제했습니다. 편집 기록에서 볼 수 있습니다.)
REV2 C (Cygwin의 GCC), 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");}
내 코드를 리팩토링 한 Dennis에게 다시 감사드립니다.
Char 상수 m[]
는 리터럴로 대체되었습니다 (리터럴을 인덱싱 할 수 있는지 몰랐습니다.)
스택 변수를 사용하여 난수 시드 (시스템에 따라 다름, 일부 시스템은 보안 조치로 메모리 할당을 임의 화)
매크로 puts
A를 매크로로 대체 printf
메시지가 내부에 배치 표시 될 때 실행해야하고 코드를 추가 printf
인수 (형식 문자열에 충분한 형식 specfiers가없는 경우 printf와 지난 몇 인수를 인쇄하지 않는다는 얼굴 촬영을 이용.) if
~로 교체되다||
새로운 매크로 안에 배치 된 플레이어 / wumpus의 새로운 위치 계산.
while
루프 외부에 배치 된 승리 / 손실 메시지 . if
조건부 연산자로 대체되었습니다.
화살표를 촬영하기 위해 라인에서 조건부 연산자 사용. 플레이어가 빠지면 메시지를 인쇄하고 범프 위치를 조정해야합니다. Dennis는 두 가지 결합 방법 printf
과 wumpus 위치 계산을 단일 표현으로 제시했지만, 나는 내 자신 중 하나와 함께 갔다. printf
인쇄 된 문자 수를 반환합니다. for Your arrow didn't hit anything\n
31 (11111 binary) 31&Q(w)==Q(w)
입니다.
이 편집에 대한 또 다른 기여는 불필요한 괄호를 제거하는 것입니다.
산출
여기서 플레이어는 Wumpus의 위치를 이미 찾았지만, 정확한 위치를 찾기 위해 철저한 탐색을 선택합니다. 게임 내에서 맹금과 구덩이가 어디에 있는지 보여준 이전의 디버그 버전과 달리, 이것은 플레이어가 방문하여 산들 바람을 느꼈던 방만 보여줍니다 (1) 맹금을 제련하거나 (2) 또는 둘 다 (3). (플레이어가 화살을 쏘고 a
놓치면, wumpus 위치 정보를 포함하는 변수 가 재설정됩니다.)
ICOSAHEDRON 표현
참고 :이 섹션은 rev 1을 기반으로합니다.
내 스타 기능! 내 코드에는 그래프가 없습니다. 작동 방식을 설명하려면 아래 세계지도를 참조하십시오. 정 이십 면체의 모든 점은 위도 0-3과 경도 0-4 (또는 단일 숫자, long*4+lat
) 로 나타낼 수 있습니다 .지도에 표시된 경도 선은 경도가 0 인 면만 통과하고 위도 선은 통과합니다. 위도가 0 인면의 중심.
플레이어는 다음과 같이 기호로 표현, 3 개 가능한 축에 지향 할 수 있습니다 남북 -
북동 - 남서 \
북서 - 남동 /
. 어떤 방이든 그는 각 축에 정확히 하나의 출구가 있습니다. 표시된 디스플레이에서 플레이어는 시계 방향으로 완전한 루프를 만듭니다. 플레이어가 어디에서 왔는지, 따라서 어디로 갈 수 있는지를 쉽게 식별 할 수 있습니다.
시작되지 않은 눈에 대해 조금 어려운 경우는 네 번째입니다. 이 극좌표 열 중 하나에 경사가 표시되면 플레이어가 경사의 바깥 쪽 끝에서 가장 가까운 극좌표 셀에서 왔으며 일반적으로 적도를 향하고 있습니다. 따라서 플레이어는 남동쪽을 향하고 있으며 그의 옵션은 15 (SOUTH, 오른쪽 셀) 25 (northEAST, 위 셀) 또는 35 (northWEST, 아래 셀)입니다.
따라서 기본적으로 정 이십 면체를 5x4 그리드에 매핑합니다. 셀은 인쇄 순서대로 19에서 0까지입니다. 아래 표에 따라 플레이어의 위도와 방향에 따라 현재 위치에서 더하거나 빼서 이동합니다.
플레이어가 보드의 아래쪽 (서쪽)을 벗어나면 맨 위 (동쪽)로 돌아오고 그 반대도 마찬가지입니다. 따라서 그의 위치는 모듈로 20이됩니다. 일반적으로 동작은 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 []의 처음 4 개 값은 위의 기능 외에도 특성 m[1]%3==m[2]%3==1
과를 갖도록 선택 m[0]%3==m[3]%3==2
됩니다. 이것은 적도 실의 방향을 그대로 남겨두고 극실에 필요한 수정을 적용합니다.
정정을위한 논리적 시간은 이동 후입니다. 그러나 문자를 저장하기 위해 이동하기 전에 수행됩니다. 따라서 m []의 특정 값을 바꾸어야합니다. 예를 들어 마지막 두 문자는 위의 표 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가 "구덩이에 영향을받지 않음"에 따라 wumpus가 시작되거나 구덩이가있는 방으로 들어갈 수 있음을 이해합니다. 일반적으로 이것은 1 점을 제외한 것을 단순화합니다. 게임이 끝났음을 나타내는 특정 변수가 없습니다. 플레이어가 부랑이나 구덩이와 일치하면 끝납니다. 따라서 플레이어가 이기면 승리 메시지가 표시되지만 구덩이를 플레이어로 이동하여 루프 밖으로 빠져 나옵니다! 나는 wumpus가있을 수 있으므로 플레이어를 구덩이에 넣을 수 없으며 원하지 않는 wumpus에 대한 메시지를 받게됩니다.
rev0 프로그램은 Visual Studio에서 완벽하게 작동했지만 IDE는 종료시 "변수 i 주위에서 스택이 손상되었습니다"라고 말했습니다. scanf와는 넣어 노력하고 있기 때문이다 int
에 char.
데니스는이 때문에 자신의 리눅스 시스템에서 잘못된 행동을보고했다. 어쨌든 그것은 개정 1에서 올바른 유형을 사용하여 고정됩니다.
rev 0에서 보드를 표시하는 줄이 어색하고 다른 플랫폼에서는 약간 다르게 나타납니다. 에서 printf(" %c%c%c")
중간 %의 C 표시되는 인쇄 가능한 문자입니다. 마지막 % c는 ASCII 0 또는 ASCII 10 (\ n, Windows에서 캐리지 리턴이있는 줄 바꿈)입니다. Windows에는 콘솔에서 작동하는 문자가 없는데 캐리지 리턴없이 줄 아래로 내려갑니다. 첫 번째 c %가 필요하지 않은 경우 (위도 1 문자 앞에 ASCII 0 또는 ASCII 9 탭이 필요합니다. 탭의 동작이 악의적으로 악의적으로 정의되지 않았습니다.) 선행 공간이 형식을 개선합니다 (위도 3 및 2 문자가 위도 1 문자에 가까워짐) .) 개정판 1에는 \ f 용지 공급 문자를 사용하는이 행의 개정판이 있으므로 printf 시작시 형식 문자가 필요하지 않습니다. 이렇게하면 짧아 지지만 Windows에서는 \ f가 작동하지 않습니다.