Random Generation?
SnowyOxygen
12 Aug 2016, 21:17Hello! I'm a bit too ambitious and I thought I'd have a go at some random generation, but I'm not entirely sure on how. Firstly does anyone know how to randomly pick a value out of a string list/dictionary? And secondly, does anyone know of any tutorials or good methods on making a generated world (or several)?
The Pixie
12 Aug 2016, 23:57Randomly picking stuff is covered in some functions on this page:
http://textadventures.co.uk/forum/samples/topic/5614/some-useful-functions
Random stuff is covered here:
http://textadventures.co.uk/forum/samples/topic/6263/generating-stuff
Generating rooms is pretty much the same (there is really no difference between a room and an object). Use "create exit" to generate the exits. The tricky bit is getting the rooms to relate to each other in a way that makes sense.
Here it is in practice.
http://textadventures.co.uk/games/view/em15b32xd0o-y-ysvgrtcg/deeper
hegemonkhan
13 Aug 2016, 21:17there's 4 built-in Randomization Functions, which Pixie has shown are to implement them into doing the fancy stuff you're asking about already, but here are the 4 Randomization Functions:
-
Dice Roll ( http://docs.textadventures.co.uk/quest/functions/corelibrary/diceroll.html )
-
GetRandomInt ( http://docs.textadventures.co.uk/quest/functions/getrandomint.html )
-
GetRandomDouble ( http://docs.textadventures.co.uk/quest/functions/getrandomdouble.html)
-
RandomChance ( http://docs.textadventures.co.uk/quest/functions/corelibrary/randomchance.html )
examples:
-
DiceRoll ("1d6") // a normal 6 sided single dice roll
-
GetRandomInt (1,10) // it "randomly" selects a number from 1 to 10, I think inclusively for both min and max: aka, including 1 and 10
-
GetRandomDouble () // it selects a value from 0 to 1, I'm not sure about this, as I've never used it lol, but I think it is exclusive: it won't pick '0.0' and not '1.0'
-
RandomChance (50) // you got a 50% random chance of returning TRUE, so a 50% chance of doing your script(s) for TRUE (and thus a 50% of doing your FALSE script/s)
if (RandomChance (50)) {
msg ("You rolled TRUE, and thus you see this message")
} else {
msg ("You rolled FALSE, and thus you see this message")
}
RandomChance (20) // you got a 20% random chance of returning TRUE, so a 20% chance of doing your script(s) for TRUE (and thus a 80% of doing your FALSE script/s):
if (RandomChance (20)) {
msg ("You rolled TRUE, and thus you see this message")
} else {
msg ("You rolled FALSE, and thus you see this message")
}
RandomChance (80) // you got a 80% random chance of returning TRUE, so a 80% chance of doing your script(s) for TRUE (and thus a 20% of doing your FALSE script/s):
if (RandomChance (80)) {
msg ("You rolled TRUE, and thus you see this message")
} else {
msg ("You rolled FALSE, and thus you see this message")
}
here's an example of a syntax for using the 'GetRandomInt' Function using a String List Attribute (I can't remember if you can do with Dictionary Attributes or not):
game.color_list = split ("red;blue;yellow", ";")
// if you don't know, Lists start at 0 for their indexing, so:
// 0 index number: red
// 1 index number: blue
// 2 index number: yellow
// 3 index number: ERROR! as there is no 4th color item in your list!
// VARIABLE = StringListItem (LIST, INDEX_NUMBER)
game.selected_color = StringListItem (game.color_list, GetRandomInt (0, ListCount (game.color_list) - 1))
and here's my guide on using lists/dictionaries, if you want to take a look at it, if you don't know how to use lists/dictionaries, and if Pixie's links don't already help with it:
http://textadventures.co.uk/forum/samples/topic/5137/list-and-dictionary-extensive-guide-by-hk
and here's me using this stuff to do travel without Exits (be warned though, the code is old, when I was just learning this stuff, so it's not the best code, lol):
http://textadventures.co.uk/forum/samples/topic/5138/explore-and-travel-code-sample-by-hk
ask if you got any questions.

Proudly Humble
15 Aug 2016, 20:25@ The Pixie
I was just looking at random rooms myself. Specifically, I want to set up random directions from one room to the next. After creating a new string list that borrowed from game.compassdirections (excluding the "in" and "out" directions), I was easily able to create a random exit direction from the first room to the second. The trouble is, "create exit" appears to be one-directional. I can exit the first room, but there is no exit out of the second room that returns to the first room.
Is there an easy way to create and pair up exits in opposite directions between rooms? Or will I have to run several IF lines to determine the return exit as I create each random exit?
The Pixie
15 Aug 2016, 21:29You must know the two rooms to connect. If you want to go from room1 to room2,:
create exit("north", room1, room2)
create exit("south",room2,room1)
hegemonkhan
15 Aug 2016, 22:08as Pixie is implying:
- you need to store/save the old location in a VARIABLE
- you need to store/save the old direction/built-in, aka 'north/south' Exit --- I don't understand Exit code/attributes/design very well, into a (another) VARIABLE
so you can then use those 2 VARIABLEs for setting-up/creating the returning Exit to that old location
to implement this, you actually need conceptually an 'old' and 'current' VARIABLE for each: your location and for your exit-direction, so you need 4 total VARIABLES. By having the 'old' and 'current' VARIABLES, enables you to transfer/preserve them, and thus the implementation will work correctly.
for a simple example:
player.parent = room // you start in 'room'
player.current_room = player.parent // room
player.old_room = player.parent // room
player.parent = room3 // you move to 'room3'
player.old_room = player.current_room // room
player.current_room = player.parent // room3
if you want to go back to the old room (room):
player.parent = player.old_room // room
player.old_room = player.current_room // room3
player.current_room = player.parent // room
or, you go to 'room5' instead:
player.parent = room5
player.old_room = player.current_room // room3
player.current_room = player.parent // room5
do you see how this 'transfering/preservation' is working by using two VARIABLES, the 'old' and 'current' concepts?

Proudly Humble
15 Aug 2016, 23:27Well, I was thinking of having the game build random exits at startup, not replace old values with new.
Here's what I had worked out for generating a random exit. Mind you, the code below is a test code in its infancy to be built upon. Its purpose was just to be a proof of concept. Once I had confirmed things were working correctly, I was going to have several rooms, each with random exits to the next (this was to be done by making an object list of the rooms involved and then using "foreach" on that object list in a startup script, applying a modified version of the below script as a function with each room as a parameter).
possible_directions = NewStringList ()
foreach (direction, game.compassdirections) {
if (direction <> "in" and direction <> "out") {
list add (possible_directions, direction)
}
}
rand = GetRandomInt(0, ListCount (possible_directions) - 1)
where = ListItem (possible_directions, rand)
create exit (where, room, room1)
Here, I make a new string list and then add all of the directions found in the string list game.compassdirections except "in" and "out". It was necessary to foreach (direction, game.compassdirections) since the game wouldn't allow me to directly set possible_directions to game.compassdirections and then remove "in" and "out" from the list because the original list was a type.
As you can tell, I then got Quest to count the number of available exit directions, generate a random number, and then generate an exit based on the random value. I used ListCount() - 1 instead of the integer 9 so that I could keep my options open later.
But... there is that return exit thing. Obviously, I could create a string of IFs to build the return exit. For instance,
if (where = "north") {
create exit ("south", room1, room)
}
I'm just wondering if there is a simpler way of coding a return exit without scripting out all ten directional possibilities. It's not a big deal if there isn't, but as long as I am learning to generate exits on the fly, I figured that I might as well as ask.
hegemonkhan
16 Aug 2016, 02:59my example of using old-current rooms was just to convey the concept of dealing with specific/previous input data that needs to be used for specific cases, though indeed it is completely tangent to your scenario.
there might be better ways, and I'm not sure if this way is neccessarily better than 'ifs' (as there's not too many to deal with for your scenario), but here's a way:
using Dictionary Attributes, which are essentially conversions (input -> output) ( http://docs.textadventures.co.uk/quest/using_dictionaries.html )
(credit to Pixie's spell library, for helping me understand dictionaries, via it dealing with opposing elemental types: fire vs water - air vs earth - etc etc etc, which is now within/apart-of his/her combat library, I think) --- this was/is hidden in the tabs/etc part of the tutorial, where you used to be able to see Pixie's code on using dictionaries for dealing with elemental types vs monster elemental (more damage, less damage, reflect/absorb damage, immune to damage, etc):
http://docs.textadventures.co.uk/quest/guides/using_types_and_tabs__advanced_.html
http://docs.textadventures.co.uk/quest/guides/using_types.html
http://docs.textadventures.co.uk/quest/guides/simple_combat_system__advanced_.html
exit generation:
create exit (where, A, B)
// returning exit generation:
create exit (StringDictionaryItem (data_object.direction_conversion_dictionary, where), B, A)
// String Dictionary:
data_object.direction_conversion_dictionary = NewStringDictionary()
dictionary add (data_object.direction_conversion_dictionary, "north", "south")
dictionary add (data_object.direction_conversion_dictionary, "south", "north")
dictionary add (data_object.direction_conversion_dictionary, "west", "east")
dictionary add (data_object.direction_conversion_dictionary, "east", "west")
dictionary add (data_object.direction_conversion_dictionary, "northeast", "southwest")
dictionary add (data_object.direction_conversion_dictionary, "northwest", "southeast")
dictionary add (data_object.direction_conversion_dictionary, "southwest", "northeast")
dictionary add (data_object.direction_conversion_dictionary, "southeast", "northwest")
dictionary add (data_object.direction_conversion_dictionary, "in", "out")
dictionary add (data_object.direction_conversion_dictionary, "out", "in")
dictionary add (data_object.direction_conversion_dictionary, "up", "down")
dictionary add (data_object.direction_conversion_dictionary, "down", "up")
// as 'creation' tag block, example:
<object name="data_object">
// I got lazy/tired on/with writing in the diagonal directions, using the abrevs for them.
<attr name="direction_conversion_dictionary" type="simplestringdictionary">north = south; south = north; west = east; east = west; up = down; down = up; in = out; out = in; nw = se; ne = sw; se = nw; sw = ne</attr>
</object>
// -----------------------------------------
// so, if we create this exit:
create exit ("north", A, B)
then in/for the returning exit's 'StringDictionaryItem', it takes 'north' and returns 'south', which is what we want for the returning exit:
// peudocode:
create exit ( {stringdictionaryitem (north) ---> returns: south } , B, A)
// another example:
create exit ("south", A, B)
// returning exit, pseudocode:
create exit ( {stringdictionaryitem (south) ---> returns: north } , B, A)
// you can of course separate out the 'stringdictionary' function, if you prefer this format/convention/style:
// returning exit:
opposite_where_variable = StringDictionaryItem (data_object.direction_conversion_dictionary, where)
create exit (opposite_where_variable, B, A)
The Pixie
16 Aug 2016, 06:49Try this:
possible_directions = Split("north,south,east,west,up,down", ",")
reverse_directions = Split("south,north,west,east,down,up", ",")
rand = GetRandomInt(0, ListCount (possible_directions) - 1)
create exit (StringListItem(possible_directions, rand), room, room1)
create exit (StringListItem(reverse_directions, rand), room1, room)
I have kind of cheated by only doing six directions, but you can add more if you want. As I listed them, I can also list the reverse direction, and use thatto create the exit going the other way.
Something to think about is whether to check if there is already an exit that way from the other room. The would depend on how you are creating the map.

Proudly Humble
16 Aug 2016, 18:09@hegemonkhan Using dictionaries is an interesting idea. I just recently started using them, but haven't found much of a use for them yet. I finally was able to understand them by realizing that the ask/tell feature is based on a dictionary. So I gave that a closer exmination. Even then, it took a while to understand. Unfortunately, due to the issues I faced with Pixie's most recent suggestion (mentioned below), I have a feeling that dictionaries are not the way to go either.
I couldn't figure out what you were doing on the old/new values, but I can toggle values on my own. I have my own clothing library that I created that relies on doing just that, based on an idea in the clothing library that Pixie created. I even borrowed the attribute name original_alias into my library to make it work. One of these days I may even upload it, but it's a definite overkill for most games, even games that use clothes.
@ The Pixie
I was initially pulling from the attribute game.compassdirections purely for the sake of accuracy. But I loved your idea of a second string with opposite values then pulling the paired values. I gave it a go, and it turns out that using game.compassdirections was more important than I was aware. I edited my code to match your idea and using those string lists caused the exits to appear in the inventory! Additionally, the exits did not appear in the compass panel. Oh, the exits did actualy work, but everyhing was a mess. I then reverted back to using game.compassdirections. All was good again.
So I decided to go ahead and simply use a list of IFs. I first thought that the string list in game.compassdirections was all over the place. It is, in fact, cleverly arranged. The indexed sum of two cardinal directions is always 7 (see code below). That makes it easy to match up opposite exits!
I did come up with a working solution for the key aspects of random exit generation. I made a simple "game" containing just the player object and eight empty rooms. The solution adds an exit from each room to the next, and the function is designed so that the script won't conflict with any pre-existing exits (if any). But there are two problems:
One, it's too random, LOL. I'll have to go back and give weights to the chances so that they aren't all equal, to make the passages feel more realistic. I think I can figure that out on my own.
Two, with regard to these generated exits, Quest isn't accpeting NE, NW, SW, and SE as directional inputs from the keyboard for this game. N, S, E, and W work correctly. The compass panel, hyperlinks, and full text (eg. "northeast") also work perfectly, including for those four diagonal directions. It's just those four abbreviated directional commands that fail, and only from the keyboard. Is this a possible bug? I've also notitced that pre-programmed exits have an attribute named alt, but exits generated midgame do not have the attribute alt (I checked this using the debugger).
Anyway, this is my current working, albeit too random solution:
Startup Script
I've preset a flag ("room") to all room objects in the game.
places = NewObjectList ()
foreach (location, AllObjects()) {
if (GetBoolean(location, "room")) {
list add (places, location)
}
}
for (iteration, 0, ListCount (places) - 2, 1) {
generate exit (ObjectListItem (places, iteration), ObjectListItem (places, iteration +1 ))
}
Function: generate exit (alpha, beta)
alpha = ObjectListItem (places, iteration)
beta = ObjectListItem (places, iteration +1 )
possible_directions = NewStringList ()
foreach (direction, game.compassdirections) {
if (direction <> "in" and direction <> "out") {
list add (possible_directions, direction)
}
}
current_exits = ScopeExitsForRoom (alpha)
foreach (portal, current_exits) {
if (ListContains (possible_directions, portal.alias)) {
list remove (possible_directions, portal.alias)
}
}
rand = GetRandomInt(0, ListCount (possible_directions) - 1)
where = ListItem (possible_directions, rand)
if (where = "northwest") {
create exit (StringListItem(game.compassdirections, 0), alpha, beta)
create exit (StringListItem(game.compassdirections, 7), beta, alpha)
}
if (where = "north") {
create exit (StringListItem(game.compassdirections, 1), alpha, beta)
create exit (StringListItem(game.compassdirections, 6), beta, alpha)
}
if (where = "northeast") {
create exit (StringListItem(game.compassdirections, 2), alpha, beta)
create exit (StringListItem(game.compassdirections, 5), beta, alpha)
}
if (where = "west") {
create exit (StringListItem(game.compassdirections, 3), alpha, beta)
create exit (StringListItem(game.compassdirections, 4), beta, alpha)
}
if (where = "east") {
create exit (StringListItem(game.compassdirections, 4), alpha, beta)
create exit (StringListItem(game.compassdirections, 3), beta, alpha)
}
if (where = "southwest") {
create exit (StringListItem(game.compassdirections, 5), alpha, beta)
create exit (StringListItem(game.compassdirections, 2), beta, alpha)
}
if (where = "south") {
create exit (StringListItem(game.compassdirections, 6), alpha, beta)
create exit (StringListItem(game.compassdirections, 1), beta, alpha)
}
if (where = "southeast") {
create exit (StringListItem(game.compassdirections, 7), alpha, beta)
create exit (StringListItem(game.compassdirections, 0), beta, alpha)
}
if (where = "up") {
create exit (StringListItem(game.compassdirections, 8), alpha, beta)
create exit (StringListItem(game.compassdirections, 9), beta, alpha)
}
if (where = "down") {
create exit (StringListItem(game.compassdirections, 9), alpha, beta)
create exit (StringListItem(game.compassdirections, 8), beta, alpha)
}
The Pixie
17 Aug 2016, 07:23I have had a look at what I actually did (should have done that first, sorry), and this is the code I used:
create exit ("west_from_" + name, null, room, room_west, "westdirection")
create exit ("east_from_" + name_west, null, room_west, room, "eastdirection")
The "westdirection" is the type of exit, and using that means you get all the aliases and alts set automatically, and so is a better way of setting it. This version of create exit
requires a name for the exit which has to be unique. The second parameter is the alias, which can be null, as above. Then the rooms from and to. The type is a string, so you can do like this:
StringListItem(game.compassdirections, 7) + "direction"
That said, I do not have any of the diagonal directions in my game. My game creates a floor of rooms at a time. Each floor is a 7x7 grid, with rooms in some cells, but not all. Once rooms are placed, it goes through the grid and places exits in two rooms are adjacent 80% of the time, which means I am only placing exits to south and west