Author Topic: Procedurally Generated Puzzle  (Read 7556 times)

SwagMode

  • Posts: 4
  • Maggot Crusher.
    • View Profile
Procedurally Generated Puzzle
« on: October 31, 2014, 04:56:44 PM »
Making a map with as many random elements as I can (so I won't have too much foreknowledge when I play it). Here's what I have for the starting room. The loot needs to be balanced, but it's the puzzle that I'd like some help/feedback for. At the moment, each button is linked to up to 4 of the runes on the ground, and each secret is linked to up to 6 of the runes. When the map first loads, it randomly disables some of those links. My greatest desire would be to set up the randomization algorithm so that it's always possible to light the entire circle of runes. There is an extra reward for lighting the whole circle, but every time I've tested it so far I get unlucky and can't light them all.

I know the rooms are too large. My wife is designing the layout of everything and will be shrinking everything down now that we've been in-game and seen it's too big.

On an unrelated note, the dungeon1.xml file was 165KB before I started scripting. ;D

EDIT: Attached the whole campaign folder. Didn't think about how useless the xml was by itself.
« Last Edit: October 31, 2014, 07:01:07 PM by SwagMode »

NekoBaron

  • Posts: 124
  • Scripting Wizard
    • View Profile
Re: Procedurally Generated Puzzle
« Reply #1 on: November 01, 2014, 01:54:52 AM »
The scripts may be a lil out of date but I released a open puzzle pack http://hammerwatch.com/forum/index.php?topic=1739.0 it contains combination puzzles and other tid bits and trap setups you might find interesting to look at, I actually create random combinations with numbers instead of other things and do some magic with counters and comparisons to check for the valid combinations.

SwagMode

  • Posts: 4
  • Maggot Crusher.
    • View Profile
Re: Procedurally Generated Puzzle
« Reply #2 on: November 01, 2014, 11:42:27 PM »
Cool puzzles, and good lesson. I hadn't thought about just randomizing the starting state of the puzzle. I've been beating myself up randomizing the mechanics. I was going to be randomizing the spawns as well. I've been playing with using a single SpawnObject that gets run however many times I need by a loop, but that leaves the monsters in a line standing shoulder to shoulder. The only solution I could think of would be to call MoveAI nodes in between each spawn, but it feels clunky. Here's the dinky monster spawn room I've been tinkering with. Most recently, I was messing with weighted spawns. Ideally, this map is the start of a survival hero arena type map. I was thinking of using global buffs to scale the difficulty more dynamically at high levels.

dingoZero

  • Posts: 50
  • Dingovania
    • View Profile
Re: Procedurally Generated Puzzle
« Reply #3 on: November 03, 2014, 07:53:12 PM »
In regards to the actor spawning, have you thought about using several different prefabs?

I know you can't get perfectly random spawns every time, but you could make say 10 different prefabs by taking the original level file, adding assorted monsters, and then deleting the doodads. You could then spawn one prefab randomly every time you need one. This could be controlled to look more organic, while still maintaining a level of randomness. I assume you could also create random puzzles with high complexity. Hell, you could even get recursive and have the prefab have random elements, you'd have a random prefab layout with a random solution/monster combination.

Also, it would be very possible to write a script in python or another language which populates a level with random monsters at random locations. I can share my python script I use to randomize my forests so they look more natural. I'm not at home right now but I could help more when I get home.

The downside of an external script is that you would need to run it every time you want to play, then you would need to pack the campaign. I suppose this could all be automated and packaged into a program which could do all of this and run the game at the click of a button.

In fact I think a guy has already done this...maybe...

dingoZero

  • Posts: 50
  • Dingovania
    • View Profile
Re: Procedurally Generated Puzzle
« Reply #4 on: November 03, 2014, 07:54:01 PM »
Now I want to start working with random stuff... lol

SwagMode

  • Posts: 4
  • Maggot Crusher.
    • View Profile
Re: Procedurally Generated Puzzle
« Reply #5 on: November 04, 2014, 03:36:55 AM »
Wow... that's even starting to go outside the scope of what I had imagined. :P So basically, use prefabs to create groups of monsters, then randomize which group spawns? That actually solves a couple of problems I was having.

Any solution that requires repacking the campaign on each use is a no go for me. However, I would like a look at that python script. It can be a pain in the ass to break up monotonous backgrounds so they look less man-made.

dingoZero

  • Posts: 50
  • Dingovania
    • View Profile
Re: Procedurally Generated Puzzle
« Reply #6 on: November 04, 2014, 04:00:53 PM »
This is in python 3.4

I'm by no means an expert programmer. It's pretty rough so let me actually add comments.

I thought about designing an interface for it so it would be useable, as well as expanding what things it can randomize. I'm pretty busy though, maybe one day. I'm definitely going to use hammerwatch randomization as a case study in the near future.

Code: [Select]
import random


def shift(x, startrange, endrange):

    #shifts a value parsed from a string in a random direction

    shiftvalue = int(random.randrange(startrange, endrange))

    inx = x

    if shiftvalue % 2 == 1:
        inx += (shiftvalue*0.01)
    else:
        inx -= (shiftvalue*0.01)
    return inx


def stringcut(string):

    #strips a line of a doodad to find the X, Y coordinate

    first = string
    second = first.split(">")
    third = second[1].split("<", 2)
    nums = third[0].split()

    return nums


def fixline(string, startrange=None, endrange=None):

    #takes the raw X, Y data from stringcut, randomizes it, and puts it back into a format readable by Hammerwatch
    if startrange is None:
        startrange = 0
    if endrange is None:
        endrange = 100

    line = stringcut(string)

    x = shift(float(line[0]), startrange, endrange)
    y = shift(float(line[1]), startrange, endrange)

    #fixline() returns a string which can be inserted into an XML document

    return "\t" + "\t" + "\t" + "\t" + '<vec2 name="pos">' + str(x) + ' ' + str(y) + '</vec2>' + "\n"


#this filepath is the location and filename you would like to read

f = open('D:/PycharmProjects/Experience/TestTxt/treetest4.xml')
linelist = f.readlines()
f.close()

j = 0
while len(linelist) > j:

    #change this string '/trees/'to whatever doodads you would like to randomize
    #you can even randomize an entire folder of doodads e.g. "/trees/" will randomize all of the trees folder

    if '/trees/' in linelist[j]:

        #if you call fixline(linelist[j+1], n, m) it will randomize the doodads in a range from n/100 to m/100
        #this is configured in a way which will take an object and shift it  a maximum of 1.5 units in any direction

        flip = fixline(linelist[j+1], 0, 150)
        linelist[j+1] = flip
    j += 1

#this filepath is the location and filename you would like to output to.
#I didn't want to modify the original file in case of an error

d = open('D:/PycharmProjects/Experience/TestTxt/output.xml', 'w')
i = 0
while len(linelist) > i:
    d.write(str(linelist[i]))
    i += 1
d.close()

If you have any questions just let me know.