最小限のNetHack


64

NetHackは、プレイヤーがダンジョンの最下層からイェンダーの魔除けを取得しなければならないローグライクゲームです。一般にtelnetを介してプレイされるゲーム全体は、ASCIIグラフィックで表されます。ゲームは非常に挑戦的であり、成功するためには多くのゲームの仕組みの知識が必要です。

この課題のために、ダンジョン全体が単一のレベルであり、5×16文字のみであると仮定します。さらに、これは「安全な」ダンジョンであるか、プロトタイプのみを実装していると想定します。モンスター、空腹に関する懸念などはありません。実際、キャラクターとアミュレットとゲームの位置のみを追跡する必要があります。プレイヤーがアミュレットと同じ場所に到着すると効果的に終了します。

チャレンジ要件

  • 5×16のダンジョン(シングルレベル)があります。
  • プレイヤーに開始場所(オプションでランダム)を与え、アミュレットにダンジョン内の別のランダム(プログラムが実行されるたびに異なる)開始広場を与えます。つまり、魔除けはプレイヤーと同じマスから始めることはできません。
  • プレーヤーを一度に1マスずつ移動する4つの入力キー(4つの基本方向)を受け入れます。他の入力の読み取り/処理が許可されます( 'enter'などを押す必要があるreadline()関数)。
  • ダンジョンの境界外への移動は許可されていません。たとえば、プレイヤーがダンジョンの右端にいる場合、右を押しても何も実行されません。
  • 初期生成後および各移動後に、ゲームの状態を印刷します。これはコードゴルフであり、印刷はかなり面白くないので、状態の変化がない仮定して、印刷関数と関数呼び出しの文字数を無視します。空のセルはピリオド(.)、アミュレットは二重引用符(")、文字はアットマーク()として表示する必要があります@
  • プレイヤーがアミュレットを「発見」するとゲームは終了します(同じ広場に到着します)

勝ち

これは、ゴルフの挑戦であり、今日から1週間の要件を満たす最短のコードが勝者と宣言されます。

基本的な要件とサンプル出力を示すC#(ungolfed)のソリューションの例を次に示します。

using System;

namespace nh
{
    class Program
    {
        static Random random = new Random();

        // player x/y, amulet x/y
        static int px, py, ax, ay;

        static void Main(string[] args)
        {
            px = random.Next(0, 16);
            py = random.Next(0, 5);

            // amulet starts on a position different from the player
            do { ax = random.Next(0, 16); } while (px == ax);
            do { ay = random.Next(0, 5); } while (py == ay); 

            print();

            do
            {
                // reads a single keypress (no need to press enter)
                // result is cast to int to compare with character literals
                var m = (int)Console.ReadKey(true).Key;

                // Move the player. Here standard WASD keys are used.
                // Boundary checks for edge of dungeon as well.
                if (m == 'W')
                    py = (py > 0) ? py - 1 : py;
                if (m == 'S')
                    py = (py < 5) ? py + 1 : py;
                if (m == 'A')
                    px = (px > 0) ? px - 1 : px;
                if (m == 'D')
                    px = (px < 16) ? px + 1 : px;

                // print state after each keypress. If the player doesn't
                // move this is redundant but oh well.
                print();

            // game ends when player is on same square as amulet
            } while (px != ax || py != ay);
        }

        static void print()
        {
            Console.Write('\n');
            for (int y=0; y<5; y++)
            {
                for (int x = 0; x < 16; x++)
                {
                    if (x == px && y == py)
                        Console.Write('@');
                    else if (x == ax && y == ay)
                        Console.Write('"');
                    else
                        Console.Write('.');
                }
                Console.Write('\n');
            }
        }
    }
}

合計文字数は1474ですが、print関数とその定義の呼び出しを無視すると、最終的な文字数はになり896ます。

プログラム実行時の出力:

................
...."...........
..........@.....
................
................

「a」キーを2回押した後の出力(上記を含む):

................
...."...........
..........@.....
................
................

................
...."...........
.........@......
................
................

................
...."...........
........@.......
................
................

10
これは@Doorknobにとって興味深いものになると感じています。
アレックスA.

10
ローグは、プレイヤーがダンジョンの最低レベルからイェンダーの魔除けを取得しなければならないオリジナルのローグライクゲームです。これを最小限のローグと呼んでみませんか?
ジル 'SO-悪であるのをやめる'

5
@Gillesなぜこれを最小限の蛇と呼ぶのですか?
ケーシークーボール

26
Psshh、斜めの動きはありませんか?yubnhjklない?魔除けを取得した後、登る階段さえありませんか?:P(とにかく猛烈に賛成票
ドアノブ

2
@tolos:ここで何がランダムであるかはまだわかりません。プログラムが80回実行される場合、プログラムが実行されるたび異なる要件を満たすことは実際には不可能です。具体的には、この回答では、魔除けは可能な79の場所のうち9つしか占有できません。それはカウントされますか?
デニス

回答:


37

TI-BASIC、42 41 38 36 35バイト

TI-83または84+シリーズのグラフ電卓用。

int(5irand→A                          //Randomize amulet position
6log(ie^(6→C                          //15.635 + 4.093i
Repeat Ans=A                          //Ans holds the player pos. (starts bottom right)
iPart(C-iPart(C-Ans-e^(igetKey-i      //Boundary check, after adjusting player position
prgmDISPLAY
End

----------
PROGRAM:DISPLAY
For(X,0,15
For(Y,0,4
Output(Y+1,X+1,".
If A=X+Yi
Output(Y+1,X+1,"¨
If Ans=X+Yi
Output(Y+1,X+1,"@
End
End

プレイヤーがどちらの方向に進むかは、押されたキーのキーコードの関数ですが、間違いなく機能する4つのキーは、上の行にあります。

Key        [Y=]  [WINDOW]  [ZOOM]  [TRACE]  [GRAPH]
           -------------------------------------------
Key code    11      12       13               15
Direction  Left     Up     Right             Down

お守りは最初の列の5つの正方形の1つから始まり、プレイヤーは右下の正方形から始まります。たとえば、可能な配置は次のとおりです。

................
¨...............
................
................
...............@

説明

プレーヤーの位置はから0+0iまでの複素数として保存され15+4i、実数部は右に、虚数部は下になります。これにより、上部と左側の境界チェックが容易になります。数値をわずかにオフセットし、ゼロに向かって丸めるだけです。たとえば、オフセットがで0.5あり、位置が-1+3i(画面の左側にある)場合、位置はあるiPart(-0.5+3.5i)=0+3iべき場所に修正されます。下と右の境界の確認は、もう少し複雑です。我々は、一定の数を減算する必要があるC程度である、15.635 + 4.093i(それは私が間に見つけることができる最短一つだ15+4i16+5iの減算、ラウンド、)C再び数、およびラウンドを反転して再び。

キーが押されると、調整されていないプレーヤーの位置はある方向に1単位移動しますが、整数部分は特定のキーが押されたときにのみ変化します。幸いなことに、動作するキーはすべて一番上の行にあります。以下は、キー11、12、13、および15が押された場合、およびキーが押されていない場合のオフセットのグラフです(押されていない場合は、中央の正方形の内側のポイントであり、整数部分は変更されません。 'オフセットには異なる整数部分があります)。C円の中心にある赤い十字です。

ここに画像の説明を入力してください

古いコード(42バイト):

int(9irand→A                     // 0≤rand≤1, so int(9irand) = i*x where 0≤x≤8
1                                //set "Ans"wer variable to 1+0i
Repeat Ans=A                     
Ans-iPart(i^int(48ln(getKey-1    //add -i,-1,i,1 for WASD respectively (see rev. history)
Ans-int(Ans/16+real(Ans/7        //ensure player is inside dungeon
prgmDISPLAY                      //display on top 5 rows of the homescreen   
                                 //(for this version switch X+Yi with Y=Xi in prgmDISPLAY)
End

制限事項

"文字をエスケープする方法はないため、a を含む文字列"はプログラム内で生成できません。したがって、¨引用符の代わりにウムラウトマークを使用します(引用符付きの既存の文字列がある場合は、表示できます)。取得する¨@プログラムでは、外部のツールが必要です。ただし、有効なTI-BASICです。


2
楽しみのために、Str1に分音記号と@記号を配置するスニペットを作成しました(「外部ツール」とは対照的です)。(、あなたの電卓と一致する一番上の行を選択し、新しいプログラムに入力して、ASMでそのプログラムを実行します。
MIライトに

ああ、電卓のマシンコードでのコーディングを忘れていました。タイプミスで私のものをクラッシュさせたくないのですが、私はそれが機能すると信じています。
リトシアスト

44

CHIP-8、48バイト

これは合法とは見なされないかもしれませんが、なぜそうではないのでしょう。仮想ゲームコンソール用のバイトコードベースのプログラミング言語であるCHIP-8でプログラムを作成しました。Octoと呼ばれるエミュレータ/デバッガーを使用して、ブラウザーで完全なプログラム(99バイト)を試すことができます。

スクリーンショット

http://johnearnest.github.io/Octo/index.html?gist=1318903acdc1dd266469

その完全なプログラムの16進ダンプは次のとおりです。

0x60 0x14 0x61 0x04 0xC4 0x3C 0xC5 0x08
0x22 0x36 0xF6 0x0A 0x22 0x52 0x40 0x00
0x12 0x16 0x46 0x07 0x70 0xFC 0x40 0x3C
0x12 0x1E 0x46 0x09 0x70 0x04 0x41 0x00
0x12 0x26 0x46 0x05 0x71 0xFC 0x41 0x10
0x12 0x2E 0x46 0x08 0x71 0x04 0x22 0x52
0x3F 0x01 0x12 0x0A 0x00 0xFD 0xA2 0x58
0xD4 0x54 0x22 0x52 0x62 0xFF 0xA2 0x5B
0xD2 0x34 0x72 0x04 0x32 0x3F 0x12 0x40
0x62 0xFF 0x73 0x04 0x33 0x14 0x12 0x40
0x00 0xEE 0xA2 0x5F 0xD0 0x14 0x00 0xEE
0xA0 0xA0 0x40 0x00 0x00 0x20 0x00 0xF0
0x90 0x90 0xD0

ASWDキーまたは元のCHIP-8キーパッドの7589キーを使用して、プレーヤーを移動できます。背景とプレーヤーを描画するためのすべてのコードとデータを削除すると、代わりに次の48バイトのダンプが取得されます。

0x60 0x14 0x61 0x04 0xC4 0x3C 0xC5 0x08
0xF6 0x0A 0x40 0x00 0x12 0x12 0x46 0x07
0x70 0xFC 0x40 0x3C 0x12 0x1A 0x46 0x09
0x70 0x04 0x41 0x00 0x12 0x22 0x46 0x05
0x71 0xFC 0x41 0x10 0x12 0x2A 0x46 0x08
0x71 0x04 0x3F 0x01 0x12 0x08 0x00 0xFD

プログラムの無制限の完全な形式は、次のように高レベルのアセンブリ言語で書かれています。

:alias px v0
:alias py v1
:alias tx v2
:alias ty v3
:alias ax v4
:alias ay v5
:alias in v6

: main
    px := 20
    py := 4
    ax := random 0b111100
    ay := random 0b001000
    draw-board
    loop
        in := key
        draw-player
        if px != 0 begin
            if in == 7 then px += -4
        end
        if px != 0x3C begin
            if in == 9 then px +=  4
        end
        if py != 0 begin
            if in == 5 then py += -4
        end
        if py != 16 begin
            if in == 8 then py +=  4
        end
        draw-player
        if vf != 1 then
    again
    exit

: draw-board
    i := amulet
    sprite ax ay 4
    draw-player
    tx := -1
    i := ground
    : draw
    loop
        sprite tx ty 4
        tx += 4
        if tx != 63 then jump draw
        tx := -1
        ty += 4
        if ty != 20 then
    again
;

: draw-player
    i := player
    sprite px py 4  
;

: amulet  0xA0 0xA0 0x40
: ground  0x00 0x00 0x20 0x00
: player  0xF0 0x90 0x90 0xD0

コンパイルされたバイト自体はCHIP-8プログラミング言語であることに注意してください。アセンブラは、このようなプログラムを作成するためのより便利な手段です。


19
仕事に最適なツール。
デニス

6
あなたのゲームを何度もプレイするのに多くの時間を無駄にさせてくれた+1
アレックスA.

4
@AlexA。さらに時間を無駄にしたい場合は、Cave Explorerを試してください。ASWDはオーバーワールドで移動し、QEはプラットフォーマーレベルでブロックをリセット/移動するために使用されます。
-JohnE

4
素晴らしい、まあ私の週末があります。
アレックスA.

1
最初は懐疑的でしたが、洞窟探検家は楽しかったし、CHIP-8は思っていたよりもずっと長い間存在していました。だから私はこれを学ばなければならないと思います。

10

Python 3、86バイト

def d():
    import sys
    for y in range(5):
        line = []
        for x in range(16):
            line.append('@' if y*16+x == p else \
                        '"' if y*16+x == a else \
                        '.')
        print(''.join(line))
    print()
    sys.stdout.flush()

p=79;a=id(9)%p
while p-a:d();p+=[p%16<15,16*(p<64),-(p%16>0),-16*(p>15)][ord(input())%7%5]

下の2行のみを数え、ドロップしd();ます。


プレーヤーを右下隅(「最後の」正方形)で開始し、最初の 79個の正方形からランダムにサンプリングすることにより、別のバイトを保存しました。
リン

申し訳ありませんが、修正されました!バイトを手動でカウントするのは驚くほどではないと思います。:<
リン

1
私はあなたが交換することにより、別の文字を保存することができると思いますa=id(9)%79a=id(9)%p
kirbyfan64sos

@ kirbyfan64sos素晴らしい!ありがとう。
リン

1
また、Python 3でこれを行う場合、raw_input呼び出しをjustに変更できますinput
kirbyfan64sos

10

C、122 121 115 104 102 101バイト

#define o ({for(int t=0;t<80;++t)t%16||putchar(10),putchar(t^p?t^a?46:34:64);})
p;main(a){for(a=1+time(0)%79;p^a;o,p+=(int[]){-16*(p>15),16*(p<64),-!!(p%16),p%16<15}[3&getchar()/2]);}

ここに初めて投稿します!気に入ってくれるといいな :)

o印刷、erm、関数です。私たちの勇敢なヒーローは、2、4、6、8で移動できますが、他の入力を送信しないように注意してください(改行なし!)。

アップデート1:もたらしたaimainのパラメータ。

更新2: OP は、入力の1つの文字列がOKであることを確認したので、それを取り除きましたscanf(これは、以前は改行をスキップしていました)。

更新3:複合配列リテラルを使用し、入力レイアウトを変更しました。無効な方向を入力すると、プログラムが動作しなくなります;)

更新4:印刷機能の呼び出しがカウントされないことに注意してください。メモを取り、ルールをより注意深く読んでください。

更新5: 1バイトが保存されました。MikkelAlan Stokkebye Christiaのおかげです。


でした!!(p%16)ことp%16>0?操作の順序を覚えていません。
リトシアスト

@ThomasKwaそれは、それ単項を-助けるだけに固執することはできませんp括弧がいずれかの方法を必要としているので、。ダブルバングは単に難読化です:)
クエンティン

@Quentin 3&getchar()/ 2 <getchar()/ 2-25
ミケルアランストッケバイクリスティ

@MikkelAlanStokkebyeChristiaありがとう:)
クエンティン

9

CJam、46 45 44 40 39 37バイト

{'.80*W$Gb'"t1$Gb'@tG/W%N*oNo}:P;
5,G,m*:Dmr~{P_l~4b2fm.+_aD&!$_W$=!}gP];

最初の行(現在のゲームの状態を出力する関数を定義する)と2番目の行のP(その関数を呼び出す)は、バイトカウントに寄与しません。

開始位置と魔除けの位置の両方が擬似ランダムに選択されます。分布は均一であり、基礎となるPRNGは許可します。

入力がありE69およびBのためにアップダウンで、Caps Lock活性化が続きますEnter

代替バージョン

{'.80*W$Gb'"t1$Gb'@tG/W%N*oNo}:P;
5,G,m*:Dmr~{P_ZYm*l~(=:(.+_aD&!$_W$=!}gP];

さらに4バイトのコストで、入力形式が大幅に改善されます。

  • このバージョンでは受け入れ824および6のためのアップダウン、テンキー上の対応する矢印キーと一致しています。
  • また、受け入れ791および3対応する斜めの移動のために。

テスト中

I / Oは対話型なので、Javaインタープリターでこのコードを試してください。

最新バージョンをダウンロードして、次のようにプログラムを実行します。

java -jar cjam-0.6.5.jar nethack.cjam

Enter各キーの後に押すのを避け、出力のインプレース更新のために、このラッパーを使用できます:

#!/bin/bash

lines=5
wait=0.05

coproc "$@"
pid=$!

head -$lines <&${COPROC[0]}

while true; do
    kill -0 $pid 2>&- || break
    read -n 1 -s
    echo $REPLY >&${COPROC[1]}
    printf "\e[${lines}A"
    head -$lines <&${COPROC[0]}
    sleep $wait
done

printf "\e[${lines}B"

次のように呼び出します。

./wrapper java -jar cjam-0.6.5.jar nethack.cjam

メインバージョン

5,G,   e# Push [0 1 2 3 4] and [0 ... 15].
m*:D   e# Take the Cartesian product and save in D.
mr~    e# Shuffle and dump the array on the stack.
       e# This pushes 80 elements. We'll consider the bottommost the amulet's
       e# position and the topmost the player's.
{      e# Do:
  P    e#   Call P.
  _    e#   Copy the player's position.
  l~   e#   Read and evaluate one line of input.
       e#      "6" -> 6, "9" -> 9, "B" -> 11, "E" -> 14 
  4b   e#   Convert to base 4.
       e#     6 -> [1 2], 9 -> [2 1], 11 -> [2 3], 14 -> [3 2]
  2f-  e#   Subtract 2 from each coordinate.
       e#     [1 2] -> [-1 0], [2 1] -> [0 -1], [2 3] -> [0 1], [3 2] -> [1 0]
  .+   e#   Add the result to the copy of player's position.
  _aD& e#   Push a copy, wrap it in an array and intersect with D.
  !    e#   Logical NOT. This pushes 1 iff the intersection was empty.
  $    e#   Copy the corresponding item from the stack.
       e#     If the intersection was empty, the new position is invalid
       e#     and 1$ copies the old position.
       e#     If the intersection was non-empty, the new position is valid
       e#     and 0$ copies the new position.
  _W$  e#   Push copies of the new position and the amulet's position.
  =!   e#   Push 1 iff the positions are different.
}g     e# While =! pushes 1.
P      e# Call P.
];     e# Clear the stack.

代替バージョン

ZYm*   e# Push the Cartesian product of 3 and 2, i.e.,
       e#   [[0 0] [0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1] [2 2]].
l~     e#   Read and evaluate one line of input.
(=     e# Subtract 1 and fetch the corresponding element of the array.
:(     e# Subtract 1 from each coordinate.

機能P

'.80*  e# Push a string of 80 dots.
W$Gb   e# Copy the amulet's position and convert from base 16 to integer.
'"t    e# Set the corresponding character of the string to '"'.
1$Gb   e# Copy the player's position and convert from base 16 to integer.
'@t    e# Set the corresponding character of the string to '@'.
G/     e# Split into chunks of length 16.
W%     e# Reverse the chunks (only needed for the alternate program).
N*     e# Join the chunks, separating by linefeeds.
oNo    e# Print the resulting string and an additional linefeed.

2
TI-BASICであなたの尻尾にぴったりです!ソリューションを確認したら投稿します。
リルトシアスト

8

Java、231バイト(関数の場合は196)

342の完全なプログラムコードは次のとおりです。

class H{public static void main(String[]a){int p=0,y=79,c;y*=Math.random();y++;p(p,y);do p((p=(c=new java.util.Scanner(System.in).next().charAt(0))<66&p%16>0?p-1:c==68&p%16<15?p+1:c>86&p>15?p-16:c==83&p<64?p+16:p),y);while(p!=y);}static void p(int p,int y){for(int i=0;i<80;i++){System.out.print((i==p?'@':i==y?'"':'.')+(i%16>14?"\n":""));}}}

印刷機能なし、231:

class H{public static void main(String[]a){int p=0,y=79,c;y*=Math.random();y++;p(p,y);do p((p=(c=new java.util.Scanner(System.in).next().charAt(0))<66&p%16>0?p-1:c==68&p%16<15?p+1:c>86&p>15?p-16:c==83&p<64?p+16:p),y);while(p!=y);}}

関数だけで問題ない場合(仕様からはわかりません)、これをもう少し196まで減らすことができます。

void m(){int p=0,y=79,c;y*=Math.random();y++;p(p,y);do p((p=(c=new java.util.Scanner(System.in).next().charAt(0))<66&p%16>0?p-1:c==68&p%16<15?p+1:c>86&p>15?p-16:c==83&p<64?p+16:p),y);while(p!=y);}

少しわかりやすくするために改行を入れて...

class H{
    public static void main(String[]a){
        int p=0,y=79,c;
        y*=Math.random();
        y++;p(p,y);
        do 
            p(
                (p=(c=new java.util.Scanner(System.in).next().charAt(0))<66&p%16>0?
                    p-1:
                    c==68&p%16<15?
                        p+1:
                        c>86&p>15?
                            p-16:
                            c==83&p<64?
                                p+16:
                                p)
                ,y);
        while(p!=y);
    }

    static void p(int p,int y){
        for(int i=0;i<80;i++){
            System.out.print((i==p?'@':i==y?'"':'.')+(i%16>14?"\n":""));
        }
    }
}

私は印刷機能数えていないよということに注意してくださいp(p,y)自体は、私は午前私はものを呼び出し文の内側に変更しておりますので、それへの呼び出しを数えます。

大文字で動作しASDWます。それらをチェックする方法のため、他のいくつかの文字も機能するかもしれませんが、異なるキーを押した場合に何が起こるべきかについて、仕様は実際には何も述べていません。


if / else ifのチェーンと同じように、同じレベルのインデントでネストされた三項演算子をフォーマットすることを好みます。より読みやすくなりました。
リルトシアスト

@ThomasKwaうん、私は時々両方の方法でそれをやったことがあります。これは、連鎖よりもネストされたifステートメントに似ています。各三元の両方の半分が同じレベルにありますが、他のものとは異なるため、私にとっては気分がいいです。
ジオビット

匿名関数が許容される場合は、192バイトにあなたの答えをトリミングすることができますvoid m()になり()->
アンク-morpork

@ dohaqatar7はい、しかし、私はJavaのコード関数にanon関数を使用していません。他の人が同じように感じるかどうかにかかわらず、それは原則として私にとって日陰になります。
ジオビット

できますかp+=
リルトシアスト

5

Java、574バイト

import java.util.*;public class N{static Random r=new Random();static int v,b,n,m;public static void main(String[] a){v=r.nextInt(16);b=r.nextInt(5);n=r.nextInt(16);m=r.nextInt(5);p();do{Scanner e=new Scanner(System.in);char m=e.next().charAt(0);if(m=='w')b=b>0?b-1:b;if(m=='s')b=b<5?b+1:b;if(m=='a')v=v>0?v-1:v;if(m=='d')v=v<16?v+1:v;p();}while(v!=n || b!=m);}static void p(){System.out.println();for(int y=0;y<5;y++){for(int x=0;x<16;x++){if(x==z && y==x)System.out.print('@');else if(x==n && y==m)System.out.print('"');else System.out.print('.');}System.out.println();}}}

基本的にC#バージョンと同じですが、難読化および最小化されています。


非常に多くの不要なスペース...;)
2015

@私はちょうどそれを修正していたでしょうか:P
フェーズ

1
また、1文字の名前と3項を使用します。
リルトシアスト

@ThomasKwa修正:D-
フェーズ

6
まだ多くの不要なスペースと三元の欠如があります;)
ウィル

5

ジュリア、161バイト

用途はwas、およびdそれぞれ、下、右、左、上に移動します。

印刷を含む完全なコード(330バイト):

B=fill('.',5,16)
a=[rand(1:5),rand(1:16)]
B[a[1],a[2]]='"'
c=[1,a==[1,1]?2:1]
B[c[1],c[2]]='@'
for i=1:5 println(join(B[i,:]))end
while c!=a
B[c[1],c[2]]='.'
m=readline()[1]
c[2]+=m=='a'&&c[2]>1?-1:m=='d'&&c[2]<16?1:0
c[1]+=m=='w'&&c[1]>1?-1:m=='s'&&c[1]<5?1:0
m∈"wasd"&&(B[c[1],c[2]]='@')
for i=1:5 println(join(B[i,:]))end
end

スコア付きコード、印刷を除く(161バイト):

a=[rand(1:5),rand(1:16)]
c=[1,a==[1,1]?2:1]
while c!=a
m=readline()[1]
c[2]+=m=='a'&&c[2]>1?-1:m=='d'&&c[2]<16?1:0
c[1]+=m=='w'&&c[1]>1?-1:m=='s'&&c[1]<5?1:0
end

ここでの違いは、ゲームの状態をマトリックスとして保存しないことです。すべての関連情報は配列cとに含まれていますa。そして、もちろん、何も印刷されません。プレイヤーがアミュレットに到達すると、ユーザーは入力を求められなくなります。


Ungolfed +説明(完全なコード):

# Initialize a 5x16 matrix of dots
B = fill('.', 5, 16)

# Get a random location for the amulet
a = [rand(1:5), rand(1:16)]

# Put the amulet in B
B[a[1], a[2]] = '"'

# Start the player in the upper left unless the amulet is there
c = [1, a == [1,1] ? 2 : 1]

# Put the player in B
B[c[1], c[2]] = '@'

# Print the initial game state
for i = 1:5 println(join(B[i,:])) end

# Loop until the player gets the amulet
while c != a

    # Put a dot in the player's previous location
    B[c[1], c[2]] = '.'

    # Read a line from STDIN, take the first character
    m = readline()[1]

    # Move the player horizontally within the bounds
    if m == 'a' && c[2] > 1
        c[2] -= 1
    elseif m == 'd' && c[2] < 16
        c[2] += 1
    end

    # Move the player vertically within the bounds
    if m == 'w' && c[1] > 1
        c[1] -= 1
    elseif m == 's' && c[1] < 5
        c[1] += 1
    end

    # Set the player's new location in B
    if m ∈ "wasd"
        B[c[1], c[2]] = '@'
    end

    # Print the game state
    for i = 1:5 println(join(B[i,:])) end

end

大丈夫だと思う

3
実際のnethackソースコードについて覚えているものと非常によく似た圧縮/難読化バージョンの+1。
ベンジャクソン

より悪いランダム化を使用すると、数バイトを節約できますa=[rand(1:5),1] c=a+1
。– lirtosiast

@ThomasKwa:常に最初の行にあるとしたら、何が楽しいですか?:)
アレックスA.

3

バッチ、329バイト

@echo off
set e=goto e
set f= set/a
%f%a=0
%f%b=0
%f%c=%random%*3/32768+1
%f%d=%random%*16/32768+1
:et
call:p %a% %b% %c% %d%
if %a%%b% EQU %c%%d% exit/b
choice/C "wasd"
goto %errorlevel%
:1
%f%a-=1
%e%
:2
%f%b-=1
%e%
:3
%f%a+=1
%e%
:4
%f%b+=1
:e
if %a% GTR 4%f%a=4
if %a% LSS 0%f%a=0
if %b% GTR 15%f%b=15
if %b% LSS 0%f%b=0
%e%t

:p
setlocal enabledelayedexpansion
::creating a new line variable for multi line strings
set NL=^


:: Two empty lines are required here
cls
set "display="
for /l %%r in (0,1,4) do (
    set "line="
    for /l %%c in (0,1,15) do (
        set "char=."
        if %3 EQU %%r (
            if %4 EQU %%c (
                set char="
            )
        )
        if %1 EQU %%r (
            if %2 EQU %%c (
                set "char=@"
            )
        )
        set "line=!line!!char!"
    )
    set "display=!display!!line!!NL!"
)
echo !display!
exit /b

これは非常に印象的です。これができることに驚いています。
eis

これは私にはダンジョンを表示せず、[W、A、S、D]?を促す一連の行のみを表示します。それは「働く」ように見えます-表示されていないダンジョンを歩くことは最終的に終了します。win7のcmd.exeを
ダンPritts

私は私が手版入力したとき-それはwitn Win7のcmd.exeの私の作品Microsoft Windows [Version 6.1.7601]
ジェリーエレミヤ

@DanPrittsそれは奇妙です。Windows 8(Microsoft Windows [Version 6.2.9200]
ankh-morpork

ああ、すべてをコピーして貼り付けたわけではなく、ウィンドウを下にスクロールしませんでした。よくやった。
ダンプリッツ

3

Perlの、228 222文字(コードの動作に不可欠ではない改行を数えていない) - 207数えていない場合printprint if、印刷のために使用されている文の部品を、しかし、ゲームロジックに追加しないでください。144(コメントでYakkが示唆したように、フィールド表現生成コードも印刷の一部として考慮する場合)

このコードは、制御に小文字のwasdを使用しています。入力はEnterで確認する必要があります。Perl 5.14.2でテスト済み。

($a=$==rand(79))+=($a>=($==rand(80)));
print $_=("."x80)=~s/(.{$=})./\1@/r=~s/(.{$a})./\1"/r=~s/(.{16})/\1\n/gr;
%r=qw(w s/.(.{16})@/@\1./s a s/.@/@./ s s/@(.{16})./.\1@/s d s/@./.@/);
while(/"/){print if eval $r{getc STDIN}}

このコードの場合、計算は正規表現を使用して印刷された表現に対して直接行われるため、計算と印刷を分離することはできません。

説明:

($a=$==rand(79))+=($a>=($==rand(80)));

このラインはプレイヤーとアミュレットの位置を決定します。プレーヤーの位置は、以下によって決定され$==rand(80)、実際に理解するのが簡単です。5×16のボードには、プレーヤーが配置できる80の異なる位置があります。位置は$=変数に保存され、保存された値が強制的に整数になります。これにより、結果を整数に明示的にキャストする必要がないため、数バイトが節約されます(rand浮動小数点値を提供します)。

位置の1つがすでにプレイヤーによって占有されているため、アミュレット用に残っているのは79の位置だけであり、したがってアミュレットの位置のために$a=$==rand(79)使用されます。繰り返しますが、割り当ては$=整数への変換を強制しますが、プレーヤーの位置$aを再利用するためにさらに割り当て$=ます。

ここで、お守りがプレイヤーと同じ位置を占めることを避けるために、その位置がプレイヤーの位置と少なくとも同じ大きさである場合、それはプレイヤーによって占有されていない場所に均一な分布を与えて1つの位置だけ進められます。これは$a = ($a >= $=)$=ここでプレイヤーの位置を保持することによって実現されます。$a$ and the onlyこの式の最初の$ = `の代わりに2つの初期割り当てを挿入することにより、最初の行が生成されます。

print $_=("."x80)=~s/(.{$=})./\1@/r=~s/(.{$a})./\1"/r=~s/(.{16})/\1\n/gr;

これにより初期フィールドが生成され、その後印刷が行われます。("."x80)80ドットの文字列を生成するだけです。=~s/(.{$=})./\1@/rその後、置き換え$=と番目の文字@、そして持つ番目の文字が。修飾子のため、それらはその場で変更しようとはしませんが、変更された文字列を返します。そのため、前の式に適用できます。最後に、16文字ごとに改行を挿入します。フィールドは、後のステートメントで暗黙的に使用できる特別な変数に保存されることに注意してください。=~s/(.{$=})./\1@/r$a"r=~s/(.{16})/\1\n/gr$_

%r=qw(w s/.(.{16})@/@\1./s a s/.@/@./ s s/@(.{16})./.\1@/s d s/@./.@/);

これにより、さまざまな移動の置換ルールを含むハッシュが作成されます。これのより読みやすいバージョンは

%r = ( 'w' => 's/.(.{16})@/@\1./s',
       'a' => 's/.@/@./',
       's' => 's/@(.{16})./.\1@/s',
       'd' => 's/@./.@/' );

キーは移動の文字であり、値は対応する置換ルールを含む文字列です。

while(/"/){print if eval"\$_=~$r{getc STDIN}"}

これがメインループです。(つまり、フィールドに)文字while(/"/)がまだあるかどうかを確認します。アミュレットに移動すると、そのキャラクターはプレイヤーキャラクターに置き換わり、フィールドから消えます。"$_

eval $r{getc STDIN}標準入力から文字を読み取り、hasから対応する置換ルールを検索し、%rそれを$_フィールド、つまりフィールドに適用します。交換が実際に行われた場合、これはtrueと評価さ(つまり、キーがハッシュで発見されたの動きが可能であった。不可能な動きが置換ルールに一致しません)。その場合printは実行されます。引数なしで呼び出されるため$_、変更されたフィールドを出力します。


1
読みやすくするために改行を含めたからといって、改行を数える必要はありません。228バイトが表示されます。また、この質問の特定の規則に従って、コードの印刷部分はバイトカウントに寄与しません。
デニス

@Dennis:印刷の部分については、今追加した説明を参照してください。私のコードでは、印刷と評価を有意義に分けることはできません。あなたの提案通りにカウントを変更しました。
celtschk

印刷コードに状態の変更はありますか?番号?さて、私の意見では、印刷コードの出力をロジックに再利用しても、ペナルティはないはずです。移動コード(これは明確です!)はカウントされますが、「表示文字列」を生成するコードはカウントされません。
-Yakk

@Yakk:コードのどの部分を印刷コードと見なしますか?(実際には、「印刷コード」が何であるかが常に明確に定義されているわけではないため、印刷コードをカウントから除外することは悪い考えでした。)
celtschk

@celtschk ("."x80)=~s/(.{$=})./\1@/r=~s/(.{$a})./\1"/r=~s/(.{16})/\1\n/grは一見かなり近いですが、私のperl-fuは数年錆びています。州の変更を見逃したかもしれません。
-Yakk

2

C#、256 248 234 227 226 225バイト

NumLockをオンにして移動するには、NumPad矢印を使用します。

わかりやすくするために、インデントとコメントを追加:

using System;
class P{
    static void Main(){
        int a=0,b=0,c,d,e;
        var r=new Random();
        while(0<((c=r.Next(16))&(d=r.Next(5))));
        Draw(a,b,c,d); // Excluded from the score.
        while(a!=c|b!=d){
            e=Console.ReadKey().KeyChar-48;
            a+=e==4&a>0?-1:e==6&a<15?1:0;
            b+=e==8&b>0?-1:e==2&b<4?1:0;
            Draw(a,b,c,d); // Excluded from the score.
        }
    }
    // The following method is excluded from the score.
    static void Draw(int a, int b, int c, int d){
        Console.Clear();
        for (int y = 0; y < 5; y++)
        {
            for (int x = 0; x < 16; x++)
            {
                Console.Write(
                    x == a && y == b ? '@' :
                    x == c && y == d ? '"' :
                                       '.'
                );
            }
            Console.WriteLine();
        }
    }
}

1
C#intsは暗黙的にゼロに初期化されると思います。また、(現時点では確認できません)キャストが問題でない場合は、文字リテラルをintに変換するか、少なくとも「a」を97に変換できます(私は思う)が、他は3桁です。

クラスフィールドのみがデフォルトで初期化されます。この場合、フィールドは静的と宣言されている必要があります。メソッド変数は、最初に使用する前に初期化する必要があります。文字数が少なくなります:4対7
Hand-E-Food

暗黙的にcharをintにキャストするヒントについて@tolosに感謝します!さらに良いことに、intとしてキャストされたConsoleKey列挙型を使用する場合、2桁の値を使用できます。
Hand-E-Food

技術的には、Mainメソッドを呼び出す必要はありませんMainので、さらに3文字を削ることができます。
ルアーン

@Luaan、あなたは間違っていると思う。C#のドキュメント:msdn.microsoft.com/en-us/library/acy3edy3.aspx
Hand-E-Food

2

Html + JavaScript(ES6)、スコアはおそらく217

あまりにも長いが、以下のスニペットでオンラインでプレイできる。

6行目(T.value ...)は出力用であり、カウントされません(ただし、出力が単純であっても、textareaの開始タグと終了タグをカウントします)

ランダム性に関しては、アミュレットは常にグリッドの右半分にあり、プレイヤーは常に左半分から始まります。

textAreaをクリックして(拡大後)、ゲームを開始および再起動します。

<textarea id=T onclick='s()' onkeyup='m(event.keyCode)'></textarea>
<script>
R=n=>Math.random()*n|0,
s=e=>m(y=R(5),x=R(8),a=R(5)*17+R(8)+8),
m=k=>(x+=(x<15&k==39)-(x>0&k==37),y+=(y<4&k==40)-(y>0&k==38),p=y*17+x,
T.value=p-a?(t=[...('.'.repeat(16)+'\n').repeat(5)],t[a]='X',t[p]='@',t.join('')):t='Well done!'
)
</script>

EcmaScript 6スニペット(Firefoxのみ)

R=n=>Math.random()*n|0
s=e=>m(y=R(5),x=R(8),a=R(5)*17+R(8)+8)
m=k=>(
  x+=(x<15&k==39)-(x>0&k==37),
  y+=(y<4&k==40)-(y>0&k==38),
  p=y*17+x,
  T.value=p-a?(t=[...('.'.repeat(16)+'\n').repeat(5)],t[a]='"',t[p]='@',t.join('')):t='Well done!'
)
<textarea id=T onclick='s()' onkeyup='m(event.keyCode)'></textarea>

EcmaScript 5スニペット(Chromeでテスト済み)

function R(n) { return Math.random()*n|0 }

function s() { m(y=R(5),x=R(8),a=R(5)*17+R(8)+8) }

function m(k) {
  x+=(x<15&k==39)-(x>0&k==37)
  y+=(y<4&k==40)-(y>0&k==38)
  p=y*17+x
  T.value=p-a?(t=('.'.repeat(16)+'\n').repeat(5).split(''),t[a]='"',t[p]='@',t.join('')):t='Well done!'
}
<textarea id=T onclick='s()' onkeyup='m(event.keyCode)'></textarea>


2

Actionscript 3:267バイト

実例はオンラインです

var a:int,p:int,t;function g(){var r=Math.random;while(p==a){a=r()*80;p=r()*80}addEventListener("keyDown",function(e){if(a==p)return;if(e.keyCode==87&&p>15)p-=16if(e.keyCode==83&&p<64)p+=16if(e.keyCode==65&&p%16>0)p--if(e.keyCode==68&&(p+1)%16>0)p++print()});print()}

ゲーム機能を使用した完全な(読みやすくするために空白が含まれている)プログラムを次に示します。

package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFormat;

    public class MiniRogue extends Sprite
    {
        var a:int, p:int, t;

        public function MiniRogue()
        {
            g();
        }

        function g(){
            var r=Math.random;
            while(p==a){
                a=r()*80;
                p=r()*80
            }
            addEventListener("keyDown",function(e){
                if(a==p)
                    return;
                if(e.keyCode==87&&p>15)
                    p-=16
                if(e.keyCode==83&&p<64)
                    p+=16
                if(e.keyCode==65&&p%16>0)
                    p--
                if(e.keyCode==68&&(p+1)%16>0)
                p++
                print()
            });
            print()
        }

        var old:int = -1;
        private function print():void {
            if (!t) {
                t = new TextField()
                t.defaultTextFormat = new TextFormat("_typewriter", 8)
                t.width=500;
                t.height=375;
                addChild(t)
            }
            var board:String = "";
            for (var i:int=0; i<80;i++) {
                if (i == p) {
                    board += "@";
                } else if (i == a) {
                    board += '"';
                } else {
                    board += ".";
                }
                if ((i + 1) % 16 == 0) {
                    board += "\n";
                }
            }
            if (a==p) {
                board += "Win!";
            }
            if (p == old) {
                board += "Bump!";
            }
            old = p;
            t.text = board;
        }
    }
}

2

Javascript:307 216

以下のスニペットでプレイできます!左側の数字は、コンソール(少なくともクロム1)が行をマージしないようにするためのものです。

コードを実行するには:

  1. 「コードスニペットを実行」を押す
  2. ctrl-shift-jを押してコンソールを開きます
  3. 結果セクションをクリックします
  4. 矢印キーを使用して再生

var x=y=2,m=Math,b=m.floor(m.random()*5),a=14,i,j,t,c=console,onload=d;function d(){c.clear();for(i=0;i<5;i++){t=i;for(j=0;j<16;j++){t+=(i==y&&j==x)?"@":(i==b&&j==a)?'"':".";if(a==x&&b==y)t=":)";}c.log(t);}}onkeydown=function(){switch(window.event.keyCode){case 37:if(x>0)x--;break;case 38:if(y>0)y--;break;case 39:if(x<15)x++;break;case 40:if(y<4)y++;break;}d();};

ゴルフをしていない:

var px=py=2,m=Math,ay=m.floor(m.random()*5),ax=14,i,j,t,c=console,onload=draw;
function draw() {
  c.clear();
  for(i=0;i<5;i++) {
    t=i;
    for(j=0;j<16;j++) {
      t+=(i==py&&j==px)?"@":
         (i==ay&&j==ax)?'"':".";
      if(ax==px&&ay==py)t=":)";
    }
    c.log(t);
  }
}
onkeydown=function() {
  switch (window.event.keyCode) {
    case 37:
      if(px>0)px--;
      break;
    case 38:
      if(py>0)py--;
      break;
    case 39:
      if(px<15)px++;
      break;
    case 40:
      if(py<4)py++;
      break;
  }
  draw();
};

編集1:ルールをより注意深く読み、それに応じてコードを書き直してください

  • お守りy値がランダム化されました
  • プレイヤーは部屋から脱出できなくなりました
  • draw関数の文字をカウントしたり、呼び出しをしたりしなくなりました

1

SpecBAS- 428 402(印刷時を除く、カウント時の466 425)

Q / A / O / Pを使用して、それぞれ上/下/左/右に移動します。

行1のダンジョンを印刷する行は、無視できる唯一の行ですが、それを少し下にゴルフしました。

1 PRINT ("."*16+#13)*5
2 LET px=8: LET py=3
3 LET ax=INT(RND*16): LET ay=INT(RND*5): IF ax=px AND ay=py THEN GO TO 3
4 PRINT AT ay,ax;#34;AT py,px;"@": LET ox=px: LET oy=py: PAUSE 0: LET k$=INKEY$
5 LET px=px+(k$="p")-(k$="o")
6 IF px<0 THEN LET px=0
7 IF px>15 THEN LET px=15
8 LET py=py+(k$="a")-(k$="q")
9 IF py<0 THEN LET py=0
10 IF py>4 THEN LET py=4
11 PRINT AT oy,ox;"."
12 IF SCREEN$(px,py)<>#34 THEN GO TO 4

#34への参照は、コードにCHR $(34)を入れる簡単な方法です。

@Thomas Kwaに感謝します。プレーヤーの開始位置がランダムであることはオプションではありませんでした。また、個別のIFステートメントを使用して、いくつかの文字を削除しました。


:あなたはあまりランダム化することにより、一部の文字を保存することができるかもしれ2 LET px=1: LET py=1: LET ax=2: LET ay=INT(RND*5)とも使用しますIF instead of ELSE IF
リトシアスト

1

別のC#、221 171 170

両方の位置がランダムなC#の別の方法を次に示します。この部分がHand-E-Foodのソリューションより7バイト長い場合でも、これを表示したかった。
Hand-E-Foodの答えは、Console.Read()を使用するとすぐに短くなります。
Consol.Readの欠点は、必要なEnterを押すと、フィールドがさらに2回印刷されることです。
しかし、(実際の)入力だけで印刷する必要があるとは思わない。

Hand-E-Foodsソリューションと同様に、ナビゲーションは8426によって行われます。

using System;
class P
{
static void Main()
{
Func<int> n=new Random().Next;
int x=n()%16,y=n()%5,a=n()%16,b,m;
while(y==(b=n()%5));

while(x!=a|y!=b)
{
Printer.Print(a, b, x, y);  // Excluded from the score.
m=Console.Read()-48;
y+=m==8&y>0?-1:m==2&y<4?1:0;
x+=m==4&x>0?-1:m==6&x<15?1:0;
}
}
}


編集:(新しい溶液を添加し、最後までPrinterClass移動)
EDIT2:(15と14を変更し、右下部に開始することによって、バイトを保存)

Maurisの技術の適応には、C#で171バイトにそれを溶融することが可能です(もちろん、両方の位置がランダムになりました):

using System;
class P
{
static void Main()
{
int p=79,a=new Random().Next()%p,m;
while(p!=a){
Printer.Print(p,a);  // Excluded from the score.
m=Console.Read()-48;
p+=m==4&p/5>0?-5:m==6&p/5<15?5:m==8&p%5>0?-1:m==2&p%5<4?1:0;
}
}
}

プリンタークラスはほぼ同じで、印刷の新しいオーバーロードです...

class Printer
{
    public static void Print(int ax, int ay, int px, int py)
    {
        Console.Write('\n');
        for (int y = 0; y < 5; y++)
        {
            for (int x = 0; x < 16; x++)
            {
                if (x == px && y == py)
                    Console.Write('@');
                else if (x == ax && y == ay)
                    Console.Write('"');
                else
                    Console.Write('.');
            }
            Console.Write('\n');
        }
    }

    public static void Print(int p, int a)
    {
        Print(p/5,p%5,a/5,a%5);
    }
}

1

ルビー、185

こちらもRubyの例です。
私はRubyにとても慣れていない、多分誰かがそれをもっとうまくやる方法を知っているだろう:)

そうしないとプログラムがクラッシュするため、lineFeedsを1としてカウントしました...

ナビゲーションは8462で行われます。Enterキーを押すたびに入力を送信する必要があります。

def display(ax,ay,px,py)
    puts
    for y in 0..4
        for x in 0..15
            if (x == px && y == py)
                print "@"
            elsif (x == ax && y == ay)
                print '"'
            else
                print '.'
            end
        end
        puts
    end
end


x=y=0
a=Random.rand(16) while y==(b=Random.rand(5))
while x!=a or y!=b
display(a,b,x,y)  # Excluded from the score.
m=gets.chomp.to_i
y-=m==8?1:0 if y>0
y+=m==2?1:0 if y<4
x-=m==4?1:0 if x>0
x+=m==6?1:0 if x<15
end

0

QBasic、103バイト

チャレンジのルールに従って、Showサブプログラムはバイトカウントに含まれず、Show p, q, a, b呼び出しも含まれません(次の改行を使用)。

b=1+TIMER MOD 9
1Show p, q, a, b
INPUT m
p=p-(m=2)*(p>0)+(m=4)*(p<4)
q=q-(m=1)*(q>0)+(m=3)*(q<15)
IF(p<>a)+(q<>b)GOTO 1


SUB Show (playerRow, playerCol, amuletRow, amuletCol)
CLS
FOR row = 0 TO 4
  FOR col = 0 TO 15
    IF row = playerRow AND col = playerCol THEN
      PRINT "@";
    ELSEIF row = amuletRow AND col = amuletCol THEN
      PRINT CHR$(34);    ' Double quote mark
    ELSE
      PRINT ".";
    END IF
  NEXT
  PRINT
NEXT
END SUB

移動するには、入力数、Enterキーを押します:1、左折し2、上がるために3右に行くこと、そして4ダウンします。

このコードは、プレイヤーが魔除けを見つけたときに、最後にゲームの状態を出力しません。それを行うにはShow p, q, a, bIFステートメントの後に別のものを追加します。

説明

ましょうabお守りの座標を表すとpqプレイヤーの座標。プレイヤーは(0、0)から始まり、アミュレットは行0から始まり、現在の時間の1の桁に基づいて、1から9までの列が含まれます。

残りは、条件付きの単なる数学の束です。覚えておくべき重要なことは、QBasicの条件が0false、-1true を返すことです。プレーヤー行更新ステートメントを見てみましょう。

p=p-(m=2)*(p>0)+(m=4)*(p<4)

場合はm=2、我々はから1を減算することにより、最大移動するp限り、p>0。同様に、場合m=4、我々は1を追加することによって、下に移動したいp限り、p<4。乗算することで目的の動作を取得できます。両方の要因がある場合は-1、その製品になります1私たちはから引くかに追加することができ、p。いずれかの条件がの0場合、製品はになり0、効果はありません。

同様に、プレイヤーが魔除けを見つけたかどうかを判断する条件は次のとおりです。

IF(p<>a)+(q<>b)GOTO 1

条件文のいずれかに該当する場合、それらの和がゼロでない(どちらかになり-1又は-2)、したがってtruthy、プログラムは一旦1行目に戻りp等しいaq等しくb、両方の条件文がされる0ので、それらの合計になります0と、制御フローに達することができますプログラムの終わり。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.