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回かかるので、彼はそれが左にあることを知っており、確実に撃ちます。
もし彼が他の方法でループしていたなら、彼はすぐにwumpusを見つけ、それが彼が回っていたのと同じ方向にあることを知っていただろう。

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ステートメントを大幅に短縮できます。
コメントでデニスからいくつかの追加のヒント、および私自身の1つ:矢印がこぶに当たったかどうかを確認するときの条件の変更:if(q==w)
> if(q-w)
(..else ..は逆です)
35%のボーナスを獲得するために、プレイヤーがしこり/ワカサギがどこにいるのか/そよ風が感じられる情報を示すグラフィック表示を追加しました。(これの古いデバッグバージョンを削除しました。これは、こぶと穴の正確な位置を示していました。編集履歴で確認できます。)
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");}
コードをリファクタリングしてくれたデニスに感謝します。
文字定数m[]
がリテラルに置き換えられました(リテラルにインデックスを付けることができるとは知りませんでした。)
スタック変数を使用した乱数のシード(システムに依存、一部のシステムはセキュリティ対策としてメモリ割り当てをランダム化します。)
で表示されるメッセージが引数内に配置されたときに実行する必要がある追加のコードと追加のコードでputs
置き換えられたマクロ(書式文字列に十分な書式指定子がない場合、printfが最後のいくつかの引数を出力しないという面の利点)と取り換えるprintf
printf
if
||
新しいマクロ内に配置されたプレーヤー/バンプの新しい位置の計算。
while
ループの外側に置かれた勝ち/負けメッセージ。if
条件演算子に置き換えられました。
矢を射るための条件付き演算子の使用。プレイヤーがミスした場合、これはメッセージの印刷とwumpus位置の調整の両方を必要とします。デニスは、2つの方法を組み合わせprintf
て、しこりの位置を1つの式に計算する方法を提供しましたが、私は自分の方法の1つを使いました。printf
は、印刷された文字数を返しますYour arrow didn't hit anything\n
。これは31(バイナリ11111)31&Q(w)==Q(w)
です。
この編集に対する私の他の貢献は、不必要な括弧の削除です。
出力
ここでは、プレーヤーはすでにWumpusの場所を見つけていますが、徹底的な探索を行って、ピットの場所を正確に見つけようとしています。ゲーム全体でくぼみと穴がどこにあるかを示した私の古いデバッグバージョンとは異なり、これはプレイヤーが訪れ、風を感じた部屋のみを表示します(1)しこりを嗅ぐ(2)または両方(3)。(プレイヤーが矢を放ってミスした場合a
、wumpus位置情報を含む変数はリセットされます。)
イコサヘドロンの表現
注:このセクションはrev 1に基づいています
私のスター機能!私のコードにはグラフがありません。仕組みを説明するには、以下の世界地図をご覧ください。二十面体上の任意の点は、緯度0〜3および経度0〜4(または単一の数字、long*4+lat
。)で表すことができます。緯度がゼロの面の中心。
プレイヤーは、次のように記号で表さ、3つの可能な軸に配向させることができます:南北-
北東-南西\
、北西-南東/
。どの部屋でも、利用可能なこれらの各軸に1つの出口があります。表示されているディスプレイでは、プレーヤーは時計回りに完全なループを作成します。一般的に、プレイヤーがどこから来たのか、したがってどこに行くことができるのかを示すプレイヤーから識別するのは簡単です。
初心者の目には少し難しい1つのケースは、4番目のケースです。これらの極列の1つに傾斜が見られる場合、プレーヤーは傾斜の外側端に最も近い極セルから来ており、一般的に赤道に向かっています。したがって、プレーヤーは南東に面しており、オプションは15(南、右のセル)25(北東、上のセル)または35(北西、下のセル)です。
したがって、基本的には、正二十面体を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
プレーヤーの入力(2桁目を削除するために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 []の特定の値は転置する必要があります。したがって、最後の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で開始します。
私は、こぶが「ピットの影響を受けない」ので、こぶが開始するか、またはピットのある部屋に入ることができることを理解しています。一般に、これは1つの点を除いて物事を単純化します。ゲームが終了したことを示す特定の変数はありません。プレーヤーがうねりや穴と一致したときに終了します。そのため、プレーヤーが勝ったとき、私は勝者のメッセージを表示しますが、ピットをプレーヤーに移動してループから抜け出します!プレイヤーにピットに入れることはできませんが、そこには不思議があり、不必要なメッセージが表示されます。
rev0programはVisual Studioで完全に動作しましたが、IDEは終了時に「変数iの周りでスタックが破損しました」と言いました。scanf関数を入れしようとしているので、これはあるint
にchar.
デニスは、このために彼のLinuxマシン上で不正な動作を報告しました。とにかく、それはrev 1で正しい型を使用することで修正されます。
rev 0でボードを表示するための行は不格好であり、他のプラットフォームでは若干異なります。でprintf(" %c%c%c")
、中央%cの表示、印刷可能な文字です。最後の%cはASCII 0またはASCII 10(\ n、Windowsではキャリッジリターン付きの改行)です。Windowsでは、コンソールで動作する文字はないようです。キャリッジリターンを与えずに1行下に移動します。最初のc%(緯度1文字の前のASCII 0またはASCII 9タブ。タブは動作が未定義であることで有名です。)先行スペースによりフォーマットが改善されます(緯度3および2文字が緯度1文字に近くなります) ) Rev 1には、\ fフォームフィード文字を使用するこの行の改訂版があるため、printfの先頭にフォーマット文字は必要ありません。これにより短くなりますが、\ fはWindowsでは機能しません。