Минимальный NetHack


64

NetHack - это игра в стиле roguelike, в которой игрок должен получить Амулет Йендора с самого низкого уровня подземелья. Обычно играется через telnet, вся игра представлена ​​графикой ASCII. Игра чрезвычайно сложна и требует знаний многих игровых механизмов, чтобы добиться успеха.

В целях этого задания предположим, что все подземелье представляет собой один уровень и всего 5 × 16 символов. Кроме того, предположим, что это «безопасное» подземелье или что вы реализуете только прототип - здесь не будет монстров, опасений по поводу голода и т. Д. На самом деле вы должны отслеживать только местоположение персонажа, амулет и игру. эффективно закончится, когда игрок прибудет в то же место, что и амулет.

Требования к вызову

  • Там будет подземелье 5 × 16 (один уровень).
  • Дайте игроку начальную позицию (необязательно случайную) и амулет в виде отдельной случайной (отличающейся при каждом запуске программы) стартовой клетки внутри подземелья. То есть амулет не может начинаться на том же поле, что и игрок.
  • Примите четыре клавиши ввода, которые перемещают игрока на одну клетку за раз (четыре основных направления). Разрешено чтение / обработка другого ввода (функция readline (), которая требует нажатия «enter» и т. Д.).
  • Путешествие за пределы подземелья запрещено. Например, если игрок находится на правом краю подземелья, нажатие вправо ничего не должно делать.
  • После первоначальной генерации и после каждого движения выведите состояние игры. Поскольку это код игры в гольф, а печать довольно неинтересна, игнорируйте количество символов для функции печати и вызова функции, не предполагая изменений состояния . Пустые ячейки должны быть показаны в виде period ( .), амулет в виде двойной кавычки ( ") и символ как в символе ( @).
  • Игра заканчивается, когда игрок «обнаруживает» амулет (прибывает на ту же площадь)

выигрыш

Это кодовый гольф-код, самый короткий код, удовлетворяющий требованиям, который через неделю с сегодняшнего дня будет объявлен победителем.

пример

Вот пример решения в 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.

Вывод при запуске программы:

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

Вывод (включая выше) после двойного нажатия клавиши «а»:

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

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

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

10
У меня есть ощущение, что это будет интересно @Doorknob.
Алекс А.

10
Rogue - оригинальная игра в жанре roguelike, в которой игрок должен получить Амулет Йендора с самого низкого уровня подземелья. Почему бы не назвать это минимальным Разбойником?
Жиль "ТАК - перестань быть злым"

5
@ Жиль Почему бы не назвать это минимальной змеей?
Кейси Кубалл

26
Понятия не по диагонали? Нет, юбнхкл? Даже не подниматься по лестнице после амулета? : P (в любом случае яростно возражает )
Дверная ручка

2
@tolos: мне все еще неясно, что здесь считается случайным . На самом деле невозможно выполнить разные требования при каждом запуске программы, если программа запускается 80 раз ... В частности, в этом ответе амулет может занимать только 9 из всех возможных 79 мест. Это считается?
Деннис

Ответы:


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

В каком направлении будет двигаться игрок, зависит от кода клавиши нажатой клавиши, но в верхнем ряду однозначно работают четыре клавиши:

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

Амулет начинается с одного из пяти квадратов в первом столбце, а игрок начинается с нижнего правого квадрата. Например, возможное расположение:

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

объяснение

Позиция игрока сохраняется как комплексное число от 0+0iдо 15+4i, где действительная часть идет вправо, а мнимая часть понижается. Это облегчает легкую проверку границ сверху и слева: мы просто слегка смещаем число и округляем до нуля. Например, если смещение равно 0.5и наша позиция -1+3i(за пределами экрана влево), то позиция будет скорректирована до того места iPart(-0.5+3.5i)=0+3i, где она должна быть. Проверка на нижнюю и правую границы немного сложнее; нам нужно вычесть число из константы C, которая составляет около 15.635 + 4.093i(это самое короткое число, которое я мог найти между 15+4iи 16+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

Ограничения

Нет возможности экранировать "символ, поэтому строки с "не могут быть сгенерированы внутри программы. Поэтому ¨вместо кавычки используется знак умляута (если бы уже существовала строка с кавычкой, я мог бы это отобразить). Чтобы попасть ¨и @в программу, нужен внешний инструмент; однако, это действительно TI-BASIC.


2
Просто для забавы я сделал этот фрагмент, который помещает диарез и символ @ в Str1 (в отличие от «внешнего инструмента»). Выберите строку в верхней части, которая соответствует вашему калькулятору, введите ее в новую программу и запустите эту программу с помощью Asm (.
MI Wright

О, я забыл о кодировании в машинном коде на калькуляторе. Не хочу разбить мою с опечаткой, но я верю, что это работает.
lirtosiast

44

ЧИП-8 , 48 байт

Это не может считаться законным, но, черт возьми, нет. Я написал свою программу на CHIP-8, языке программирования на основе байт-кода для виртуальной игровой консоли. Вы можете попробовать полную программу (99 байт) в своем браузере, используя эмулятор / отладчик, который я написал под названием Octo:

Скриншот

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

Шестнадцатеричный дамп этой полной программы выглядит следующим образом:

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 или клавиш 7589 на оригинальной клавиатуре CHIP-8. Если я удаляю весь код и данные для рисования фона и проигрывателя, я вместо этого получаю этот 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

Обратите внимание , что скомпилированный байт сами являются язык программирования ЧИП-8; ассемблер - просто более удобный способ составления таких программ.


19
Правильный инструмент для работы.
Деннис

6
+1 за то, что заставляю меня тратить кучу времени, играя в вашу игру снова и снова.
Алекс А.

4
@AlexA. Если вы хотите потратить еще больше времени, попробуйте Cave Explorer . ASWD перемещается в сверхмире, а QE используются для сброса / перемещения блоков на уровнях платформера.
JohnE

4
Отлично, хорошо, вот и мои выходные.
Алекс А.

1
Сначала я был настроен скептически, но Cave Explorer был забавным, а 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]

Только считая две нижние строки и сбрасывая d();.


Я сохранил еще один байт, запустив игрока в правом нижнем углу («последний» квадрат), а затем произвел случайную выборку из первых 79 квадратов.
Линн

Ой, извините, исправлено! Полагаю, я не умею считать байты вручную. : <
Линн

1
Я думаю, что вы можете сохранить другой символ, заменив a=id(9)%79на a=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это печать, эм, функция. Наш храбрый герой может перемещаться с 2, 4, 6 и 8, но остерегайтесь не посылать никаких других входных данных (никаких новых строк!).

Update 1: принес aи iв main«параметры с.

Обновление 2: OP подтвердил, что с одной строкой ввода все в порядке, я избавился scanf(от которой я пропустил перевод строки).

Обновление 3: Использовал литерал составного массива и изменил формат ввода. Программа теперь выходит из строя, если вы введете неверное направление;)

Обновление 4: заметил, что вызов функции печати не считается. Взял на заметку, чтобы прочитать правила более внимательно.

Обновление 5: один байт сохранен, благодаря Миккель Алан Стоккебье Кристиа.


Может !!(p%16)быть p%16>0? Я не помню свой порядок операций.
lirtosiast

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

Первая строка (определяет функцию, которая печатает текущее состояние игры), а P во второй строке (вызов этой функции) не влияют на количество байтов.

И начальная позиция, и позиция амулета выбираются псевдослучайно. Распределение является столь же равномерным, и основной PRNG позволяет.

Ввод является E, 6, 9и Bдля Up , вниз , влево и вправо , с Caps Lockактивировано, а затем Enter.

Альтернативная версия

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

За счет еще четырех байтов формат ввода значительно улучшен:

  • Эта версия принимает 8, 2, 4и 6для Up , вниз , влево и вправо , что соответствует соответствующие клавиши со стрелками на цифровой клавиатуре.
  • Он также принимает 7, 9, 1и 3для соответствующих диагональных ходов.

тестирование

Поскольку ввод-вывод является интерактивным, вы должны попробовать этот код с интерпретатором 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! Я опубликую, как только я проверю свое решение.
lirtosiast

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. Это более читабельно.
lirtosiast

@ThomasKwa Да, я иногда делал это обоими способами. Это больше похоже на вложенные операторы if, чем на цепочку. Мне лучше, потому что обе половины каждой троицы находятся на одном уровне, но отличаются от других.
Geobits

Если анонимная функция приемлема, вы можете урезать свой ответ до 192 байт: void m()становится()->
ankh-morpork

@ dohaqatar7 Да, но я не использую аноновые функции Java для гольф-кода. Мне принципиально не по себе, неважно, чувствуют ли другие то же самое.
Geobits

Вы можете сделать p+=?
lirtosiast

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 #, за исключением запутанного и свернутого.


так много лишних пробелов ...;)
будет

@ Я просто исправлю это: P
фаза

1
Также используйте однобуквенные имена и троичные.
lirtosiast

@ThomasKwa исправлено: D
фаза

6
Есть еще много ненужных мест и нехватка троих;)
Будет

5

Юлия, 161 байт

Пользы w, a, sи 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
+1 за сжатую / запутанную версию, очень похожую на ту, что я помню о реальном исходном коде nethack.
Бен Джексон

Вы можете сохранить несколько байтов, используя худшую рандомизацию:a=[rand(1:5),1] c=a+1
lirtosiast

@ThomasKwa: Что это весело, если он всегда на первой линии? :)
Алекс А.

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
Дэн Притц

Это работает для меня с Win7 cmd.exe - когда я набираю ver, я получаюMicrosoft Windows [Version 6.1.7601]
Джерри Иеремия

@DanPritts Это странно. Он отлично работает для меня в Windows 8 ( Microsoft Windows [Version 6.2.9200])
ankh-morpork

Я не копировал и не вставлял все это, я не прокручивал окно вниз. Отлично сработано.
Дэн Притц

3

Perl, 228 222 символа (не считая перевода строки, которые не являются неотъемлемой частью работы кода) - 207, если не считать printиprint if операторы, которые используются для печати, но не добавляют логику игры; 144, если учесть также код генерации представления поля как часть печати, как предложил Якк в комментариях)

Этот код использует нижний регистр для управления; ввод должен быть подтвержден нажатием 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предоставляет значение с плавающей запятой).

Поскольку одна из позиций уже занята игроком, для амулета остается только 79 позиций, поэтому для позиции амулета $a=$==rand(79)используется. Опять же, присвоение $=заставляет преобразование в целое число, однако я дополнительно назначаю его $aдля повторного использования.$= для позиции игрока.

Теперь, чтобы амулет не занимал ту же позицию, что и игрок, он продвигается на одну позицию, если его позиция, по крайней мере, такая же, как позиция игрока, обеспечивая равномерное распределение по местам, не занятым игроком. Это достигается , $a = ($a >= $=)когда $=здесь имеет место игрока. Теперь первая строка генерируется путем вставки двух начальных присваиваний вместо первого $a$ and the only$ = `в этом выражении.

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

Это создает начальное поле, а затем печатает. ("."x80)просто генерирует строку из 80 точек. =~s/(.{$=})./\1@/rзатем заменяет $=й символ с @, а =~s/(.{$=})./\1@/rв $aй символ с ". Благодаря rмодификатору они не пытаются изменить на месте, а возвращают измененную строку, поэтому их можно применять к предыдущим выражениям. Наконец, =~s/(.{16})/\1\n/grвставляет новую строку каждые 16 символов. Обратите внимание, что поле хранится в специальной переменной, $_которая может быть неявно использована в последующих инструкциях.

%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: Какую часть моего кода вы считаете печатным кодом? (На самом деле мое мнение таково, что исключение кода печати из подсчета было плохой идеей именно потому, что не всегда четко определено, что такое «код печати».)
celtschk

На ("."x80)=~s/(.{$=})./\1@/r=~s/(.{$a})./\1"/r=~s/(.{16})/\1\n/grпервый взгляд @celtschk довольно близок, но мое перл-фу несколько лет заржавело. Я мог пропустить изменение состояния там.
Якк

2

C #, 256 248 234 227 226 225 байт

Для перемещения используются стрелки NumPad с включенным NumLock.

Отступы и комментарии для ясности:

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 неявно инициализировать в ноль. Также (не могу проверить в данный момент), если приведение не является проблемой, вы можете преобразовать символьные литералы в целые, или, по крайней мере, «а» в 97 (я думаю), хотя остальные три цифры.

По умолчанию инициализируются только поля класса, и в этом случае требуется, чтобы они были объявлены статическими. Переменные метода должны быть инициализированы перед первым использованием. Это занимает меньше символов: 4 против 7.
Hand-E-Food

Спасибо @tolos за подсказку о неявном приведении char к int! Еще лучше, если я использую приведение ConsoleKey к типу int как int, я могу использовать двухзначные значения.
Hand-E-Food

Технически, Mainметод не должен вызываться Main, так что вы можете сбрить еще три символа.
Луана

@Luaan, я думаю, ты ошибаешься. Документация по C #: msdn.microsoft.com/en-us/library/acy3edy3.aspx
Hand-E-Food

2

HTML + JavaScript (ES6), оценка может быть 217

Слишком длинная, но играбельная онлайн в фрагментах ниже.

Строка 6 (T.value ...) предназначена для вывода и не учитывается (но для простоты я посчитал теги открытия и закрытия текстовой области, даже если они тоже выводятся)

Что касается случайности: амулет всегда находится в правой половине сетки, а игрок всегда начинает в левой половине.

Нажмите на текстовую область (после ее увеличения), чтобы запустить и перезапустить игру.

<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 Snippet (только 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

Вы можете поиграть в фрагмент ниже! Цифры слева только для того, чтобы консоль (по крайней мере, chrome) не объединяла строки.

Чтобы запустить код:

  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();};

Un-golfed:

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: Внимательно прочитайте правила и переписайте мой код соответственно

  • значение амулета у теперь рандомизировано
  • игрок больше не может покинуть комнату
  • Я больше не считаю символы в функции рисования или вызовы к ней

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

1

Другой C #, 221 171 170

Вот еще один способ в C # с обеими случайными позициями. Хотел показать это, даже если эта часть на 7 байт длиннее, чем решение Hand-E-Food.
Ответ Hand-E-Food, конечно, будет короче, как только он будет использовать Console.Read ().
Недостатком Consol.Read является то, что нажатие требуемой клавиши Enter приводит к тому, что поле будет напечатано еще 2 раза.
Но я не думаю, что есть требование печатать только на (реальном) вводе.

Навигация осуществляется 8426 как в решении Hand-E-Foods.

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: (изменил 14 на 15 и сохранил байт, начиная с правого нижнего

края ). Приспосабливая технику Mauris, можно уменьшить его до 171 байта в C # (конечно сейчас без обеих случайных позиций):

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. Вам нужно каждый раз отправлять ввод с помощью ввода.

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, bпосле IFоператора.

объяснение

Позвольте a, bпредставить координаты амулета и p,q координаты игрока. Игрок начинает игру с (0, 0), а амулет начинается со строки 0 со столбцом от 1 до 9 включительно, основываясь на цифре 1 текущего времени.

Остальное - просто математика с условными обозначениями. Важно помнить, что условные выражения в 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) и, следовательно, верна, и программа вернется к строке 1. Как только pравны aи qравны b, оба условия будут 0, поэтому их сумма будет, 0и поток управления может достичь конец программы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.