범퍼 사냥


39

내가 젊은이 였을 때, 아이들은 컴퓨터 상점으로 걸어 가서 직원들이 우리를 쫓아 낼 때까지 Wumpus Wumpus를 할 것입니다. 그것은 1970 년대 중반의 가정용 컴퓨터에서 프로그래밍 할 수있는 간단한 게임 이었기 때문에 병아리 크기의 마이크로 프로세서 대신에 일부는 아마도 실제 병아리를 가지고 있다고 생각합니다.

최신 하드웨어에서 게임을 재현하여 그 시대를 떠올려 봅시다.

  1. 플레이어는 정 이십 면체지도의 무작위 방에서 시작합니다 (따라서 20 개의 방이 있으며 정이 십면의면처럼 서로 연결되어 있으며 모든 방에는 정확히 3 개의 출구가 있습니다).

  2. wumpus는 무작위로 선택된 다른 방에서 시작됩니다. 플레이어는 악취의 방향을 결정하는 것이 불가능하지만, 점퍼가 악취가 나고, 그 위치에 인접한 3 개의 방에서 악취가 감지 될 수 있습니다. 이 게임은 "당신은 부랑자 냄새가 나"고만보고합니다.

  3. 플레이어는 활과 무한한 수의 화살을 가지고 있으며, 그는 언제든지 자신의 방으로 쏠 수 있습니다. 고비가 그 방에 있으면 죽고 플레이어가 이깁니다. 혹이 그 방에 없으면 깜짝 놀랐고 현재 위치에 연결된 3 개의 방 중 하나로 무작위로 움직입니다.

  4. 무작위로 선택된 하나의 방 (플레이어가 시작하는 방이 아닌 것으로 보장됨)에는 밑이없는 구덩이가 있습니다. 플레이어가 구덩이 근처의 방에 있으면 산들 바람을 느끼지만 산들 바람이 어느 문에서 왔는지는 알 수 없습니다. 그가 구덩이와 함께 방으로 들어 오면 죽고 부랑자가 이깁니다. 부랑자는 구덩이에 영향을받지 않습니다.

  5. 플레이어가 wumpus의 방으로 들어가거나 wumpus가 플레이어의 방으로 들어가면 wumpus가 이깁니다.

  6. 플레이어는 자신이 향하고있는 방향을 숫자 (1 = 오른쪽, 2 = 왼쪽, 3 = 뒤로)로 지정한 다음 동작 (4 = 화살표 쏴, 5 = 지정된 방향으로 걷기)을 지정합니다.

  7. 득점을 위해 각 게임 문자열 ( "바람을 느낀다", "당신은 부랑자를 냄새 맡는다", "당신의 화살이 아무 것도 맞지 않았다"등)을 1 바이트로 간주 할 수 있습니다. 텍스트에서 게임 코드를 숨기려면 이것을 남용하지 마십시오. 이것은 플레이어와의 상호 작용을위한 것입니다.

  8. 메가 뱃트를 구현하기 위해 바이트 수의 10 %를 공제하십시오. 메가 뱃은 플레이어와 다른 임의의 방에서 시작합니다 (왜냐하면 방랑자와 / 또는 구덩이와 방을 공유 할 수는 있음). 플레이어가 방망이로 방에 들어간 경우, 방망이는 무작위로 선택된 다른 방 (구덩이 또는 부랑자가있는 방이 아닌 것으로 보장됨)으로 자신의 새로운 무작위 위치로 비행하기 전에 플레이어를 운반합니다. 방망이 옆에있는 3 개의 방에서는 삐걱 거리는 소리가 들리지만 플레이어는 소리가 어느 방에서 나오는지에 대한 정보를 얻지 못합니다.

  9. icosahedral map과 pit, wumpus, bats (해당되는 경우)의 위치에 대해 지금까지 플레이어가 가지고있는 정보를 나타내는 그래픽 인터페이스를 구현하기 위해 바이트 수의 35 %를 차감합니다. 플레이어. 분명히, 부 푸스가 움직이거나 플레이어가 박쥐에 의해 움직인다면, 그에 따라지도를 재설정해야합니다.

  10. 조정 된 가장 낮은 바이트 수가 이깁니다.

이 버전의 게임에 대한 기본 소스 코드 (위의 규칙을 준수 할 필요는 없으며, 어쨌든 완전히 풀리지 않음)는 이 웹 사이트 및 다른 사이트 에서 찾을 수 있습니다 .


몇 가지 설명이 있습니다 : 3. 혹이 그 방에 없으면 깜짝 놀랐고 3 개의 방 중 하나로 옮깁니다. 그래서 화살을 쏴서 놓으면 혹이 와서 죽일 수 있습니까? 그리고 wumpus는 깜짝 놀랐을 때만 움직일 것입니다. 그렇지 않으면 그냥 그대로 유지됩니까? 6. 플레이어의 제목은 그가 온 방에 의해 결정된다는 것을 이해합니다. 만약 그가 남쪽에서 온다면 그의 선택은 1.northeast 2.northwest 3.south이고 북쪽에서 온다면 반대가 될 것입니다. 또한 귀하의 규칙은 참조 프로그램 (단순히 자세히 조사하지 않은)보다 단순 / 골 피어처럼 보입니다. 맞습니까?
Level River St

아아! 찾을 수없는 어떤 그물에 20 면체 어디의 이중 그래프의 사진을.
Jack M

1
@steveverrill 그렇습니다. 망치지 않으면 움직이지 않습니다. 게임에는 많은 변형이 있습니다. 예를 들어, 많은 버전에서 화살표가 원을 그리며 죽일 수 있습니다. 나는 그것을 파싱했다.
Michael Stern

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}
Michael Stern

2
@JackM 아니오, "뒤로"는 돌아 서서 돌아 오는 길을 걷는 것을 의미합니다. "뒤로"를 두 번 누르면 시작한 곳이됩니다. 초기 게임 상태를 저장할 필요가 없습니다.
Michael Stern

답변:


21

골프 스크립트, 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. Bash에서 GolfScript로 교수님의 답변 을 이식 했습니다. 점수 : 269

  2. 의견에서 Peter Taylor 의 제안에 따라 행동했습니다 . 점수 : 250

  3. Peter Taylor는 전체 코드를 리팩터링 하고 조회 테이블을 압축하는 데 도움을주었습니다. 점수 : 202

  4. 인접한 방의 룩업 테이블을 수학적 접근 방식으로 대체했습니다. 점수 : 182

  5. 수학적 접근을 지원하는 리팩토링 된 입력, 출력 및 기능. 점수 : 163

큰 "감사합니다!"는 Peter Taylor에게 모든 도움을 요청합니다.

작동 원리

20 개의 방은 십이 면체의 꼭지점으로 표시되며 다음과 같은 방식으로 0에서 19까지의 숫자가 지정됩니다.

십이 면체 그래프

N에 인접한 방을 찾아 시계 방향으로 정렬하려면 네 가지 경우를 고려해야합니다.

  • 경우 N ≡ 0 모드 4 (청색 정점), 인접한 공간은 N - (19) , N + 2 개조 (20)N - 2 개조 20 .

  • 경우 N ≡ 1 개 모드 4 (녹색 정점), 인접한 공간은 N - (19) , N - 모드 4 (20)N + 4 개조 20 .

  • 경우 N ≡ 2 모드 4 (노란색 정점), 인접한 공간은 N - (19) , N - 모드 2 (20)N + 2 개조 20 .

  • 경우 N ≡ 3 개조 4 (적색 정점), 인접한 공간이고, 19 - N , N + 4 개조 20N - 4 개조 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을 절약 할 수 있습니다 Q19rand 97+; 2 @97%3*&>..., 인라인으로 추가 1 Q{19rand 97+}2*:,\:H대체하여, 몇 가지 |*을 할 수있는 가장 좋은 방법은 자주 인 if. B목적이 없으며 스택을 사용하여 몇 가지 변수를 더 제거 할 수 있다고 생각합니다.
피터 테일러

1
조회 테이블에 대한 기본 변환이라는 또 다른 빈번한 트릭을 언급하는 것을 잊었습니다. 인접 목록에 대한 62 개의 문자를 33 개의 문자열로 대체 할 수 있습니다 256base 20base(+/- 97도 제거 할 수 있음). 유일한 단점은 인쇄 할 수없는 문자가 필요하다는 것입니다.
피터 테일러

1
나는 더 관용적 인 GS (주로 변수가 아닌 스택을 사용)로 리팩토링하여 13 절약 했습니다 . 출력을 덜 예쁘게 만드는 데 추가로 10이 추가됩니다. 그것은 이전 주석에서 언급 된 조회 테이블 압축과 다릅니다.
피터 테일러

1
전혀, 나는 그것을 즐겼다. 룩업 테이블 접근 방식이 내가 사용하려는 수학적 방법보다 훨씬 낫다는 것에 실망했습니다. BTW 나는 현재 버전에 작은 버그가 있다고 생각합니다. 화살표를 발사하고, 놓치고, 혹을 방으로 놀라게 You were killed by the wumpus하면 화살표가 언급되지 않은 채로 출력 됩니다. 그것이 내가 예쁜 버전이 아닌 이유입니다.
피터 테일러

1
2*2+=>)2*
Peter Taylor

15

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에게 대단히 감사합니다. 이 컴파일러는 includerev 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[]는 리터럴로 대체되었습니다 (리터럴을 인덱싱 할 수 있는지 몰랐습니다.)

스택 변수를 사용하여 난수 시드 (시스템에 따라 다름, 일부 시스템은 보안 조치로 메모리 할당을 임의 화)

매크로 putsA를 매크로로 대체 printf메시지가 내부에 배치 표시 될 때 실행해야하고 코드를 추가 printf인수 (형식 문자열에 충분한 형식 specfiers가없는 경우 printf와 지난 몇 인수를 인쇄하지 않는다는 얼굴 촬영을 이용.) if~로 교체되다||

새로운 매크로 안에 배치 된 플레이어 / wumpus의 새로운 위치 계산.

while루프 외부에 배치 된 승리 / 손실 메시지 . if조건부 연산자로 대체되었습니다.

화살표를 촬영하기 위해 라인에서 조건부 연산자 사용. 플레이어가 빠지면 ​​메시지를 인쇄하고 범프 위치를 조정해야합니다. Dennis는 두 가지 결합 방법 printf과 wumpus 위치 계산을 단일 표현으로 제시했지만, 나는 내 자신 중 하나와 함께 갔다. printf인쇄 된 문자 수를 반환합니다. for Your arrow didn't hit anything\n31 (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와는 넣어 노력하고 있기 때문이다 intchar.데니스는이 때문에 자신의 리눅스 시스템에서 잘못된 행동을보고했다. 어쨌든 그것은 개정 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가 작동하지 않습니다.


1
나는 글쓰기를 좋아한다.
Michael Stern

수정의 내가 (교체의 첫 번째 포함 제거 리눅스에 GCC로 컴파일 할 수 있도록했기 때문에 나는 확실히 그것을인지하지 않다 scanf_s함께 scanf하고 포함 stdio.h나는 C보다 C ++ 평가자로 컴파일하는 경우),하지만 대한 잘 작동을하지 않습니다 나를. 예를 들어, 왼쪽으로 가면 처음 ( 15 35) 오른쪽으로 돌아 가면 처음 시작 했던 방과 다른 방에 있습니다.
Dennis

@Dennis 종료시 오류의 원인을 추적했습니다. 그것은 32 비트 정수라고 가정하는 것을 char에 넣으려고 할 때 "변수 i 주위의 스택을 손상시킵니다"는 scanf_s (아마도 안전합니다!)입니다. 따라서 내가 제안하는 첫 번째 것은 scanf가 "% d"에 사용하는 유형을 확인하고 변수 i를 해당 유형으로 변경하는 것입니다. char에 대한 종료 오류가있는 정답, int에 대한 종료 오류가없는 정답 및 Microsoft 유형 __int64의 잘못된 대답 ( "% lld"를 넣지 않으면 오랫동안 동일합니다)도 얻습니다. ungolfed 버전과 디스플레이에 문제가 있습니까?
Level River St

@ steveverrill : 예, 두 가지 버전을 모두 시도했습니다. 의 유형 i은 실제로 문제입니다. 매뉴얼 페이지 말한다 : " d는 선택적 부호 첨부 10 진수 정수를 일치, 다음 포인터에 대한 포인터해야 INT ." 유형을 변경하면 제대로 작동합니다.
Dennis

@ steveverrill : VS가 물건을 처리하는 방법을 모르겠지만 GCC로 컴파일하면 (C ++이 아닌 C로) 많은 문자를 저장할 수 있습니다. 로 교체 하거나 로 교체 NULL하는 경우 포함이 필요 하지 않으며, 이전에 필요하지 않으며 메인 밖으로 이동 하거나 외부로 이동할 수 있습니다 (기본값 은으로 초기화 됨 ). 또한, 당신은 정의 할 수 있습니다 교체 로 모든 인스턴스에 대한 매크로를 정의 할 수 있어야한다 . 0scanf_sscanfintmainidint0p=19,h=rand()%p,w=rand()%pm[]*mif(...==...)puts(...);
Dennis

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

첫 번째 작업 코드는 다음과 같습니다. 골프를 좀 더하기 위해 나중에 다시 올 것이다.
Howard

내 코드는 현재 1 자 더 깁니다. 더 골프를 치는 방법을 찾고 있습니다!
Timtech

회원님이 내 도움이 필요하지만 정의하여 14 개 문자를 저장할 수 있습니다 {puts}:|;대체하여, 5 개 문자를 R하고 W함께 -하고 >놓기로 9 개 문자 (주변 공간을 제거 할 수 있습니다) '> 'print(질문에서 요구하지 않는 것).
데니스

@Dennis 감사합니다. 나는 당신의 제안 중 일부를 반드시 이행 할 것입니다.
Howard

9

JavaScript (ECMAScript 6) -2197 1759 -45 % = 967.45 자

거의 골프를 마치고 ...

완전한 보너스를 위해 Icosahedral Map과 Mega-Bats가있는 GUI를 포함합니다.

범퍼 스 GUI

  • 각 방에는 4 개의 버튼이 있습니다 : X(구덩이); B(메가 밧); W(Wumpus); 그리고 P(당신).
  • 현재 위치는 파란색으로 표시됩니다.
  • 해당 객체가 해당 위치에있을 수 있으면 버튼이 빨간색으로 표시되고 해당 위치에 있지 않으면 녹색으로 표시됩니다.
  • WP버튼은 현재 위치에 인접한 객실에서 클릭 할 수 있습니다.
  • 이기면 배경이 녹색으로 바뀌고 죽으면 배경이 빨간색으로 바뀝니다.

암호:

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)

클로저 컴파일러를 사용하여 ECMA 6없이 1066을 얻을 수 있습니다.
AMK

사물이 어디에 있는지 공제하는 데 도움이되는 그래픽 표현이있을 때 얼마나 쉬울 지 궁금합니다. 1+하지만 :) 약간 너무 쉽게
Sylwester

9

배쉬, 365 (첫 번째 작업 버전 726!)

골프 스크립트를 따라 잡기?

@Dennis는 기본적으로 모든 골프를 수행했습니다. 감사!

프로그램은 유효한 입력을 가정합니다. 올바른 입력은 선택한 방향 (오른쪽 1, 왼쪽 2, 뒤로 3)과 동작 (4-쏴, 5)입니다.

일부 설명

나는 보통 큰 장황한 설명을한다. 그러나 이것은 내가 너무 귀찮게하기에는 너무 복잡하다.

십이 면체 그래프의 각 정점은 문자 (a = 1, b = 2, ... t = 20)로 인코딩됩니다.

플레이어의 시작 위치는 항상 20입니다 (그리고 그들은 18로 돌아와 서 있습니다). 그 자체로는 중요하지 않기 때문에 플레이어, 구덩이 및 부랑자의 상대 위치 만 중요하기 때문입니다.

변수 $p는 플레이어의 위치를 ​​저장합니다. $r플레이어의 이전 위치를 저장합니다. $wwumpus이고 $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. @DigitalTrauma의 제안에서 2 개의 문자를 저장했습니다.
  10. 부랑자가 오른쪽에있을 때만 쏴 버릴 수있는 주요 버그가 수정되었습니다. 같은 문자 수.
  11. 를 사용하여 2 개의 문자를 제거하기 위해 매개 변수 확장을 사용했습니다 $m.
  12. 도랑 grep과 약간 더 현명한 방법으로 많은 숯을 깎았습니다 .
  13. Cif 문에 사용할 정규식 검색 함수와 E"넌 범퍼를 죽였다"를 인쇄하고 종료하는 함수로 정의 됩니다 .
  14. "if statement"재배치로 1 개의 문자를 저장했습니다.
  15. 를 제거하여 많은 문자를 저장하고 d불필요한 괄호를 제거했습니다.
  16. 버그 수정. 많은 문자 추가 :(
  17. 무어 세이빙 스 ( http://xkcd.com/1296/ )
  18. @Dennis의 또 다른 아이디어 (몇자를 절약)와 교묘 한 간접 사용 (1 자 절약).
  19. q ()의 스타일 수정.
  20. 적절한 출력을 다시 추가

샘플 런

"In :"이 입력되고 "Out :이 출력됩니다".

플레이어는 잠시 돌아 다니며 부랑자 냄새를 맡고 쏴 버립니다. 그들은 그리워하고, 부비는 그들의 방으로 와서 먹습니다.

에서 : 15

에서 : 15

에서 : 25

에서 : 25

에서 : 15

아웃 : 당신은 고비 냄새

에서 : 14

아웃 : 당신이 그리워

Out : 부랑자가 널 먹었어


1
코드를 100 바이트 이상 짧게 만들 수 있다고 생각합니다. 1. exit1 바이트보다 길기 때문에 g=10이 아닌 문장 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}몇 바이트를 저장 둘.
Dennis

1
교체 while :;do... donefor((;;);{... }3 문자 절약을 위해
디지털 외상에게

1
@ professorfish : 함수가 현재 문자열 그립 방식보다 더 효과적이라고 생각합니다. 예를 들어, d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}당신의 정의를 대체 할 수 snd $pd $w. 당신이 또한 정의하면 u=${s#*$r}$s(그리고 정의 조정 l하고 f그에 따라)를, 당신은 필요하지 않습니다 $k$m더 이상. 83 바이트를 절약한다고 생각합니다. 또한 공간 q ()이 필요하지 않습니다.
Dennis

1
@professorfish : 그리고 c(){ [[ $1 =~ $2 ]];}예를 들어 두 번째 줄부터 마지막 ​​줄까지를 정의 하고 바꾸면 3 바이트를 더 절약 할 수 있습니다 c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis

1
@professorfish : 내가 제안한 기능을 사용하면 3 바이트 더 짧아야합니다. 당신은 이후 네 줄을 대체하여 106 추가 바이트를 저장할 수 b=$pd $p;u=u${s#*$r}$s, 후 라인 read iy=${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().
Dennis

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];

마지막으로 Dennis의 룩업 테이블 버전을 따라 잡았습니다. 이 버전의 흥미로운 점은 룸 레이아웃에 대한 조회 테이블이 없다는 것입니다.

정 이십 면체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의입니다.

순서 (2)의 15 순열 A_5에 존재하고, 각각에 대해 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%=35있습니다. 입력이 인 경우 길이가 3 인 배열의 네 번째 요소에 액세스하려고합니다 .
Dennis

@Dennis, 예, 잠자리에 든 후 깨달았습니다. 나는 그것을 고치는 다양한 방법을 생각할 수있다. 모든 비용은 2이다.
Peter Taylor

로 한 문자를 다시 얻을 수 있습니다 9/3%@3%=.
Dennis

나는 현재 좀 더 과감한 구조 조정으로 7 자입니다. 그러나 그 9/대신 1 문자가 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 Huntus를 쉽게 구현하도록 설계 되지 않았습니다 . 대신, 나는 삼각형의 주제를 중심으로 언어를 먼저 디자인하고, 정 이십 면체 데이터 구조로 끝났고, 그 때문에 Wumpus라고 부르기로 결정했습니다.

그렇습니다. Wumpus는 주로 스택 기반이지만 20 면체의면 주위에 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 으로 설정되어 있습니다. 해당 얼굴의 1 비트를 설정하여 wumpus를 인코딩하고 2 비트를 설정하여 pitus를 인코딩합니다. 이렇게하면 둘 다 같은 방에있을 수 있습니다. 플레이어의 위치는 정 이십 면체에 전혀 기록되지 않으며 대신 항상 얼굴이 활성화됩니다 (한 번에 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.

이것은 우리가 세 번 반복하는 루프입니다. 매번 우리는 오른쪽 이웃을보고 위험이있는 경우 적절한 줄을 인쇄 한 다음 정 이십 면체를 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-큰

이것은 내가 기대 한 것만 큼 짧지는 않았지만 그래프 처리에 약간 다른 접근법을 취했기 때문에 어쨌든 ungolfed 버전을 게시하고 있습니다.

오리엔테이션을 유지하는 회전 상태의 정 이십 면체 (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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.