Minimal NetHack

CHIP-8, 48 bytes

This may not be considered legal, but why the hell not. I wrote my program in CHIP-8, a bytecode-based programming language for a virtual game console. You can try the complete program (99 bytes) in your browser using an emulator/debugger I wrote called Octo:

Screenshot

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

A hex dump of that complete program is as follows:

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

You can move the player with the ASWD keys, or the 7589 keys on the original CHIP-8 keypad. If I remove all the code and data for drawing the background and the player, I instead get this 48 byte dump:

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

The ungolfed, complete form of the program was written in a high level assembly language as follows:

: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

Note that the compiled bytes themselves are the CHIP-8 programming language; the assembler is simply a more convenient means of composing such programs.


TI-BASIC, 42 41 38 36 35 bytes

For your TI-83 or 84+ series graphing calculator.

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

Which direction the player will go is a function of the key code of the key pressed, but four keys that definitely work are these on the top row:

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

The amulet starts on one of the five squares in the first column, and the player starts at the bottom right square. For example, a possible arrangement is:

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

Explanation

The player's position is stored as a complex number from 0+0i to 15+4i, where the real part goes to the right and the imaginary part goes down. This facilitates easy boundary checking on the top and left: we simply offset the number slightly and round towards zero. For example, if the offset is 0.5 and our position is -1+3i (off the screen to the left), then the position will be corrected to iPart(-0.5+3.5i)=0+3i, where it should be. Checking for the bottom and right boundaries is slightly more complicated; we need to subtract the number from a constant C, which is about 15.635 + 4.093i (it's the shortest one I could find between 15+4i and 16+5i), round, subtract from C again to flip the number back, and round again.

When a key is pressed, the unadjusted player position will move by 1 unit in some direction, but the integer part only changes when certain keys are pressed. Luckily, the keys that work are all on the top row. Below is a graph of the offsets in cases where keys 11, 12, 13, and 15 are pressed, and when no key is pressed (No press is the point inside the center square, causing the integer parts to be unchanged; the four keypresses' offsets have different integer parts). C is the red cross at the center of the circle.

enter image description here

Old code (42 bytes):

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

Limitations

There is no way to escape a " character, so strings with a " cannot be generated inside a program. Therefore, this uses the umlaut mark ¨ instead of a quote (if there were an already-existing string with a quote mark, I could display that). To get ¨ and @ in a program, an outside tool is required; however, it is valid TI-BASIC.


Python 3, 86 bytes

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]

Only counting the bottom two lines, and dropping d();.

Tags:

Code Golf