views:

356

answers:

7
+1  Q: 

Tree Algorithm

I was thinking earlier today about an idea for a small game and stumbled upon how to implement it. The idea is that the player can make a series of moves that cause a little effect, but if done in a specific sequence would cause a greater effect. So far so good, this I know how to do. Obviously, I had to make it be more complicated (because we love to make it more complicated), so I thought that there could be more than one possible path for the sequence that would both cause greater effects, albeit different ones. Also, part of some sequences could be the beggining of other sequences, or even whole sequences could be contained by other bigger sequences. Now I don't know for sure the best way to implement this. I had some ideas, though.

1) I could implement a circular n-linked list. But since the list of moves never end, I fear it might cause a stack overflow ™. The idea is that every node would have n children and upon receiving a command, it might lead you to one of his children or, if no children was available to such command, lead you back to the beggining. Upon arrival on any children, a couple of functions would be executed causing the small and big effect. This might, though, lead to a lot of duplicated nodes on the tree to cope up with all the possible sequences ending on that specific move with different effects, which might be a pain to maintain but I am not sure. I never tried something this complex on code, only theoretically. Does this algorithm exist and have a name? Is it a good idea?

2) I could implement a state machine. Then instead of wandering around a linked list, I'd have some giant nested switch that would call functions and update the machine state accordingly. Seems simpler to implement, but... well... doesn't seem fun... nor ellegant. Giant switchs always seem ugly to me, but would this work better?

3) Suggestions? I am good, but I am far inexperienced. The good thing of the coding field is that no matter how weird your problem is, someone solved it in the past, but you must know where to look. Someone might have a better idea than those I had, and I really wanted to hear suggestions.

+1  A: 

What you're describing sounds very similar to the technology tree in a game live Civilization.

I don't know how the Civ authors built theirs, but I'd be inclined to use a multigraph to represent possible 'moves' - there will be some you can start at with no 'experience', and once you're in them, there will be multiple paths through to the end.

Draw-out what potential options you can have at each stage of the game, and then draw lines going from some options to others.

That should give you a start on implementation, as graphs are [relatively] easy concepts to implement and utilize.

warren
The civilization technology tree is one-way only and has a defined end (future tech). My tree must be circular with no defined end. Even though you could assume beelines as circular trees, in that tech tree you can't cross the same node more than once.
Leahn Novash
+2  A: 

You might want to implement a state machine anyway, but you don't have to hardcode state transitions.
Try to make a graph of states, where link between state A to state B will mean A can lead to B.
Then you can traverse graph at runtime to find where player goes.

Edit: You can define graph node as:
-state-id
-list of links to other states, where every link defines:
-state-id
-precondition, a list of states what must be visited before going to this state

Sergey Volegov
That might work, but the states aren't really hard defined, so I am not sure. I mean, A-B-C could cause an effect, but B-C wouldn't. That's why I mentioned in my post that I might have to had duplicated nodes to deal with such situations. The idea looks good, though. Care to elaborate?
Leahn Novash
And how would I verify the preconditions easily?
Leahn Novash
You just keep track of already visited nodes in a stack, then check each precondition agains recently visited nodes
Sergey Volegov
I'd have to make the stack a circular list to avoid stack overflow, but I guess we are arriving at something here.
Leahn Novash
+1  A: 

Sounds like a neural network. You could create one and train it to recognize the patterns that cause the various effects you are looking for.

Jason Z
But then wouldn't it be very hard to add new patterns?
Leahn Novash
That is extremeley more complex than it needs to be.
Paxinum
+1  A: 

What you're describing sounds somewhat similar to a dependency graph or a word graph. You might look into those.

Looks like dependency graph is a good call, but I couldn't find any reliable article about word graph. Can you help me?
Leahn Novash
+2  A: 

I'm not absolutely completely sure that I understand exactly what you're saying, but as an analagous situation, say someone's inputting an endless stream of numbers on the keyboard. '117' is a magic sequence, '468' is another one, '411799' is another (which contains the first one).

So if the user enters:

55468411799

you want to fire 'magic events' at the *s:

55468*4117*99*

or something like that, right? If that's analagous to the problem you're talking about, then what about something like (Java-like pseudocode):

MagicSequence fireworks = new MagicSequence(new FireworksAction(), 1, 1, 7);
MagicSequence playMusic = new MagicSequence(new MusicAction(), 4, 6, 8);
MagicSequence fixUserADrink = new MagicSequence(new ManhattanAction(), 4, 1, 1, 7, 9, 9);

Collection<MagicSequence> sequences = ... all of the above ...;

while (true) {
  int num = readNumberFromUser();
  for (MagicSequence seq : sequences) {
    seq.handleNumber(num);
  }
}

while MagicSequence has something like:

Action action = ... populated from constructor ...;
int[] sequence = ... populated from constructor ...;
int position = 0;

public void handleNumber(int num) {
  if (num == sequence[position]) {
    // They've entered the next number in the sequence
    position++;
    if (position == sequence.length) {
       // They've got it all!
       action.fire();
       position = 0; // Or disable this Sequence from accepting more numbers if it's a once-off
    }
  } else {
    position = 0; // missed a number, start again!
  }
}
Cowan
You got the idea right, but I am still trying to swallow your code. Gimme 5 minutes to understand what it does.
Leahn Novash
Your idea works, but it wouldn't scale. Think about 100 sequences, think about 1000, with every single one of them checking every character. It might work on desktop but it would be deadly. Anyway, I will keep the idea in mind. We might be able to improve upon it.
Leahn Novash
In this toy example, I doubt it matters. 100 array lookups? 1000? 1000 x nearly nothing = nearly nothing. I know your situation won't be so simple, so it may not suit; but I'm sure you saw the idea here was to demo the idea of letting sequences manage their own state, rather than doing it centrally.
Cowan
You could also use a database to represent the sequences you want to match and have it follow Cowan's pattern, searching them and returning matching sequences.
Dave DuPlantis
Dave has an interesting idea... if the longest sequence is 10 'numbers', keep the last 10 user's numbers (say 1234567890) in a list and then do from the DB something like SELECT * FROM magicSequence WHERE '%' + sequence LIKE '1234567890'. Again, toy example but an idea.
Cowan
+1  A: 

create a small state machine for each effect that you'd want. at each user action, 'broadcast' it to all state machines. most of then won't care, but some will advance, or maybe go backwards. when one of them reaches it's goal, produce the desired effect.

to keep the code neat, don't hardcode the state machines, instead build a simple data structure that encodes the state graph: each state is a node with a list of interesting events, each one points to the next state's node. Each machine's state is simply a reference to the appropriate state node.

edit: It seems Cowan's advice is equivalent to this, but he optimises his state machines to express only simple sequences. seems enough for your specific problem, but more complex conditions could need a more general solution.

Javier
There could be more than one "next state". Ideas?
Leahn Novash
that means spawning a new machine
Javier
+1  A: 

@Cowan, @Javier: Nice idea, mind if I add to it?

Let the MagicSequence objects listen to the incoming stream of user input, that is notify them of the input (broadcast) and let each of them add the input to there internal input stream. This stream is cleared when the input is not the expected next input in the pattern that would have the MagicSequence fire its action. As soon as the pattern is completed, fire the action and clear the internal input stream.

Optimize this by only feeding input to the MagicSequences that are waiting for it. This could be done two ways:

  1. You have an object that lets all MagicSequences connect with events that correspond with numbers in their patterns. MagicSequence(1,1,7) would add itself to got1 and got7, for example:

    UserInput.got1 += MagicSequnece[i].SendMeInput;

  2. You could optimize this such that after each input MagicSequences deregister from invalid events and register with valid ones.

Ralph Rickenbach
Great, that's a more optimised general case, as long as the overhead of the registration/dereg is less than the cost of just broadcasting. Of course you can have info flow back the other way too -- 'Actions' could 'register' NEW actions back with the controller to 'unlock' new actions, etc.
Cowan