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:
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.
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();
.