14
2
Text adventure games have a pretty set formula; there's a world composed of a series of rooms / spaces, the player can move around these rooms, and there are some items in the rooms. Items can be picked up by the player, put down, used to access other rooms (e.g. keys), and combined with other items to make new items.
Challenge
Your challenge is to write a text adventure runtime in the fewest bytes (code golf). To keep things simple, all you need to do is output a truthy or falsey value depending on whether a given series of commands would win a given game or not (no interactivity, no human friendly output, etc.)
Game Rules
- The world is always composed of a corridor with 10 connected rooms. Each room requires a key to enter, but can be exited at any time without a key (so it's some kind of latch lock I guess);
- The player begins in room 0, and wins if they ever enter room 9 (once they reach room 9 they can do whatever they like, including go to another room, and they will still have won);
- Each room can contain any number of items;
- There are up to 26 items, named A-Z, and no item will appear more than once in the world;
- The player can pick up items from the current room and place them in their inventory (they can also drop items from their inventory into the current room);
- The player's maximum inventory size is finite, and will be provided with the level details;
- At the beginning of the game, the player's inventory is always empty;
- There is no limit to the maximum number of items in a room (though the implicit limit would be 26, since that is the total number of items);
- Items A-J are keys which can be used to enter rooms 0-9 (i.e. the player can move to room 0 if they have item A, to room 1 if they have B, etc. note that keys are not required to leave a room, and the player begins in room 0, so the key "A" is only required if the player wants to return to room 0);
- Items in the player's inventory can be combined to create new items (which will be created in the player's inventory) — the permitted combinations will be provided with the level details;
- Combining items consumes the original items (i.e. if one of the items was a key, it will no-longer be possible to use that key);
- If the player tries to do something impossible (e.g. pick up an item which is not in the current room / drop an item they don't have / combine items they don't have / go to a room they don't have the key for), nothing happens and they can continue;
- The player will never give a nonsense command (e.g. go to room 11).
So a simple game might look like this:
v
+---+---+---+---+---+---+---+---+---+---+
| C | | J | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
| CORRIDOR |
+---------------------------------------+
Inventory capacity: 99
Room 0 contains item "C" (which is the key to room 2). Room 2 contains item "J" (which is the key to room 9). The player can win the game by picking up C, moving to room 2, picking up J, then moving to room 9.
A more complex game might be:
v
+---+---+---+---+---+---+---+---+---+---+
| C | | X |YZ | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
| CORRIDOR |
+---------------------------------------+
Inventory capacity: 10
C+X => D
Y+Z => J
Now the player can win by picking up C, moving to room 2, picking up X, combining C with X to create D, then moving to room 3. They can now pick up and combine Y and Z to get J, allowing them to go to room 9.
Input Format
There's a fair bit of input to handle, and that's a pretty boring task, so the input format is very flexible. You will get the following data, and how it should be sent to your program is largely up to you:
- The initial contents of each room (list of 0 or more items for each room);
- A collection of permitted item combinations (each contains 2 input items and their output item — note that the input items are unordered);
- The maximum inventory size (integer, 0 <= size <= 26);
- The list of commands the player attempted.
The player's commands can be:
[P]ick up <item>
- picks up an item from the room and puts it into the player's inventory (if there is space)[D]rop <item>
- drops an item from the player's inventory into the current room[C]ombine <item1> <item2>
- combines 2 items in the player's inventory to produce a new item[G]o to <room>
- travels to the chosen room if the player has the required key
For example, the input format I used for testing was simple program arguments:
./adventure YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CYZ G9
# r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 combinations inv. size commands...
# means:
# room 0 starts with items Y & Z, all other rooms start empty
# 1 combination is possible: Y+Z => J
# max inventory size is 2
# player commands are [P]ick up Y, [P]ick up Z, [C]ombine Y and Z, [G]o to room 9
# (in this example, the player wins)
But if some other format makes it easier, that's fine (e.g. special delimiter characters / multiple lines / different ordering / serialised to JSON / etc.)
Output Format
Your program should return some truthy output if the player's commands cause them to win the game, and some falsey output otherwise. This could be a recognisable message to stdout, a program return code, or whatever your language of choice provides. All other output will be ignored.
Test Cases
The following bash script provides a test harness which will check most situations. It has been written to use the format described above, but modifying it to use a different format is just a case of adding a conversion in the invoke
function.
#!/bin/sh
PROG="$1";
if [[ -z "$PROG" ]]; then
echo "Usage: $0 <program-to-test>";
exit 1;
fi;
function invoke {
"$PROG" "$@"
}
RED="\033[1;31m";
GREEN="\033[1;32m";
RESET="\033[m";
FAILURES="0";
function pass {
if ! invoke "$@" >/dev/null 2>&1; then
echo "${RED}Expected pass, got fail:${RESET} $*" >&2;
(( FAILURES = "$FAILURES" + 1 ));
invoke "$@" 2>&1;
fi;
}
function fail {
if invoke "$@" >/dev/null 2>&1; then
echo "${RED}Expected fail, got pass:${RESET} $*" >&2;
(( FAILURES = "$FAILURES" + 1 ));
invoke "$@" 2>&1;
fi;
}
echo "Running tests...";
# R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 C I Cmd...
pass J '' '' '' '' '' '' '' '' '' 0 9 PJ G9;
fail '' J '' '' '' '' '' '' '' '' 0 9 PJ G9;
pass J '' '' '' '' '' '' '' '' '' 0 9 PJ PJ G9;
fail J '' '' '' '' '' '' '' '' '' 0 9 PJ;
fail J '' '' '' '' '' '' '' '' '' 0 9 G9;
pass J '' '' '' '' '' '' '' '' '' 0 9 G9 PJ G9;
pass J '' '' '' '' '' '' '' '' '' 0 1 PJ G9;
fail J '' '' '' '' '' '' '' '' '' 0 0 PJ G9;
fail J '' '' '' '' '' '' '' '' '' 0 9 PJ DJ G9;
fail J '' '' '' '' '' '' '' '' '' 0 9 PJ PJ DJ G9;
pass J '' '' '' '' '' '' '' '' '' 0 9 PJ DJ PJ G9;
pass J '' '' '' '' '' '' '' '' '' 0 9 PJ DJ PJ G9;
pass B CJ '' '' '' '' '' '' '' '' 0 2 PB G1 DB PC PJ G9;
fail B CJ '' '' '' '' '' '' '' '' 0 2 PB G1 DB PB PC PJ G9;
pass AJ '' '' '' '' '' '' '' '' '' 0 2 PA PJ G9;
pass B D '' J '' '' '' '' '' '' 0 2 PB G1 PD G3 DB PJ G9;
fail B D '' J '' '' '' '' '' '' 0 2 PB G1 PD G2 DB PJ G9;
fail B D '' J '' '' '' '' '' '' 0 2 PB G1 PD G3 PJ G9;
fail B D J C '' '' '' '' '' '' 0 2 PB G1 PD G3 PJ G9;
pass AJ '' '' '' '' '' '' '' '' '' 0 2 PA PJ G9 G0;
fail ADJ '' '' '' '' '' '' '' '' '' 0 3 PA PD PJ G3 DJ G0 PJ G9;
pass ADJ '' '' '' '' '' '' '' '' '' 0 3 PA PD PJ G3 DJ G0 G3 PJ G9;
fail ADJ '' '' '' '' '' '' '' '' '' 0 3 PA PD PJ G3 DJ G0 DD G3 PJ G9;
pass ADJ '' '' '' '' '' '' '' '' '' 0 3 PA PD PJ DD G3 DJ G0 DD G3 PJ G9;
fail ADJ '' '' '' '' '' '' '' '' '' 0 1 PA DA DA PD PJ G9;
pass ADJ '' '' '' '' '' '' '' '' '' 0 1 PA DA DA PJ G9;
fail ABCDEFGHIKLMNOPQRSTUVWXYZ J '' '' '' '' '' '' '' '' 0 26 PA PB PC PD PE PF PG PH PI PJ PK PL PM PN PO PP PQ PR PS PT PU PV PW PX PY PZ G9;
pass ABCDEFGHIJKLMNOPQRSTUVWXYZ '' '' '' '' '' '' '' '' '' 0 26 PA PB PC PD PE PF PG PH PI PJ PK PL PM PN PO PP PQ PR PS PT PU PV PW PX PY PZ G9;
fail YZJ '' '' '' '' '' '' '' '' '' 0 2 PY PZ CYZ PJ G9;
pass YZJ '' '' '' '' '' '' '' '' '' 1 YZW 2 PY PZ CYZ PJ G9;
pass YZJ '' '' '' '' '' '' '' '' '' 1 YZW 2 PY PZ CYZ PJ CWJ G9;
fail XYZJ '' '' '' '' '' '' '' '' '' 1 YZW 2 PY PZ CYZ PX PJ G9;
fail XYZJ '' '' '' '' '' '' '' '' '' 1 YZW 2 PY PZ CYZ PX DY DZ PJ G9;
pass XYZJ '' '' '' '' '' '' '' '' '' 1 YZW 2 PY PZ CYZ PX DW PJ G9;
pass YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CYZ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 CYZ G9;
pass YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CYZ CYZ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CYZ DJ CYZ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CYZ DJ PY PZ CYZ G9;
fail WZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PW PZ CYZ G9;
fail WZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CYZ G9;
pass YZ '' '' '' '' '' '' '' '' '' 1 YZJ 2 PY PZ CZY G9;
pass YZ '' '' '' '' '' '' '' '' '' 1 ZYJ 2 PY PZ CYZ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 1 PY PZ CYZ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 1 PY PZ CYZ PY PZ CYZ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 1 PY PZ CYZ PJ G9;
fail YZ '' '' '' '' '' '' '' '' '' 1 YZJ 1 PJ G9;
pass BW UV '' '' '' '' '' '' '' '' 3 BUR WVS RSJ 2 PB PW G1 DW PU CBU DR PW PV CVW PR CRS G9;
fail BW AUV '' '' '' '' '' '' '' '' 3 BUR WVS RSJ 2 PB G1 PU CBU DR PA PB G0 DA PW G1 PV CVW PR CRS G9;
pass BCW AUV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU DR PC PA PB G0 DA PW G1 DB PV CVW PR CRS G9;
fail BCW UV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU DR PC PA PB G0 DA PW G1 DB PV CVW PR CRS G9;
fail BCW AUV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU PA PB G0 DA PW G1 DB PV CVW PR CRS G9;
fail BCW AUV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU DR PA G0 DA PW G1 DB PV CVW PR CRS G9;
fail BCW AUV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU DR PB G0 DA PW G1 DB PV CVW PR CRS G9;
fail BCW AUV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU DR PA PB G0 DA G1 DB PV CVW PR CRS G9;
fail BCW AUV '' '' '' '' '' '' '' '' 3 CUR WVS RSJ 2 PB PC G1 DB PU CCU DR PA PB G0 DA PW G1 DB CVW PR CRS G9;
pass BFK LG M N O CDE PQR U W '' 10 BPT CQS TSH HUI IWV VFA GRX MXZ ANY YZJ 5 \
PB PF PK G1 PL PG G6 DB DK DL G5 PC PD PE G6 DF G2 PM G6 DM DC G3 PN G4 PO G6 DN DO DD DE \
PB PP CBP PC PQ CCQ CTS G7 PU CUH G8 PW CWI G6 PF CVF PR PM PN CGR CMX CAN CYZ G9
fail BFK LG M N O CDE PQR U W '' 10 BPT CQS TSH HUI IWV VFA GRX MXZ ANY YZJ 5 \
PB PF PK G1 PL PG G6 DB DK DL G5 PC PD PE G6 DF G6 DM DC G3 PN G4 PO PM G6 DN DO DD DE \
PB PP CBP PC PQ CCQ CTS G7 PU CUH G8 PW CWI G6 PF CVF PR PM PN CGR CMX CAN CYZ G9
if (( "$FAILURES" == "0" )); then
echo "${GREEN}All tests passed${RESET}";
else
echo "${RED}Total failures: $FAILURES${RESET}";
fi;
Winning
Standard code golf: shortest code (in bytes) wins. Entries must follow the game rules, which in practice means they must pass all test cases (more tests may be added if necessary).
You might not believe me but I thought of a challenge that is pretty much the same thing as this one a few days ago... – acrolith – 2016-08-06T16:15:35.663
I like this challenge. However, I'd definitely include test cases outside of your test script. – Nathan Merrill – 2016-08-06T16:18:55.293
@NathanMerrill can do, what format would you prefer? (the test cases inside the script are already quite easy to parse so I wasn't sure what to do to make a test table without simply repeating the same lines!) – Dave – 2016-08-06T16:25:06.563
@daHugLenny I got the idea a few days ago too. I guess it's possible we were both inspired by some challenge posted last week, or another question on the network. I can't remember where I got the idea from. – Dave – 2016-08-06T16:26:39.120
Dropping an item the user does not have. Is it impossible (no op) or nonsense (will not happen). And dropping a not existing item? – edc65 – 2016-08-06T16:42:38.420
Dropping an item the user doesn't have is a no-op. Nonsense only covers commands which would never be possible (e.g.
P@
,D7
,QA
, etc.). If the command could be possible in some game, but isn't possible now / in this game, it's a no-op. To be clear; I'm only saying nonsense won't happen to avoid the need for bounds-checking, etc. in all answers. – Dave – 2016-08-06T17:05:12.520@edc65 I updated to clarify, but also see my comment above. – Dave – 2016-08-06T17:07:49.753
Is combining AB the same as combining BA? If yes, do we have to account for this? – orlp – 2016-08-06T17:57:12.707
@orlp yes, it is the same and you will need to account for it (some of the tests already check that) – Dave – 2016-08-06T18:11:11.853
The first example (beginning "So a simple game might look like this:") seems to assume that the player starts with
A
in their inventory, but I don't see this stated anywhere. Can you edit to clarify? – Peter Taylor – 2016-08-06T19:36:27.330@PeterTaylor the player starts in room 0, so A is not needed at all – edc65 – 2016-08-06T19:43:23.097
@edc65, yes, that makes sense. The rules are somewhat weird, though, so some emphasis would help. – Peter Taylor – 2016-08-06T19:49:13.900
Does the code need to take into account a room containing more than one item? For example, room 1 contains item X, player enters room 1 and drops item Y. – trichoplax – 2016-08-06T19:52:05.870
@PeterTaylor I have added some clearer explanations of keys. I designed it that way so that submissions can be simpler (moving only requires checking one key, which depends on the destination and nothing else). – Dave – 2016-08-06T19:54:03.020
@trichoplax yes rooms can contain multiple items, but there will never be more than one of the same item, no matter what the player does. I'll update an example to show that more obviously. – Dave – 2016-08-06T19:55:10.147