Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Likelihood of using attacks #489

Open
ohrrpgce-bugbot opened this issue Nov 1, 2007 · 9 comments
Open

Likelihood of using attacks #489

ohrrpgce-bugbot opened this issue Nov 1, 2007 · 9 comments
Labels
attacks new feature Something new that we would like to add

Comments

@ohrrpgce-bugbot
Copy link

[bz#489]

It sure would be jolly good if you could choose the likelihood of a certain attack being used by an enemy. Whether through percentages or choices like "very likely" or "rarely", elaboration on enemy attacks would be pretty cool.

From: onlyoneinall
Severity: feature request

@ohrrpgce-bugbot
Copy link
Author

Comment author: msw188

This is already partially doable by giving the enemy the option for attack A in more slots than attack B. So the six attack slot limit is the main limiting factor there.

@ohrrpgce-bugbot
Copy link
Author

Comment author: @0ion9

If the developers agree that this explicit control is a good approach, then I have some tested Python code that could easily be translated to implement this facility. I think that explicit control to this level probably isn't a good thing, that it requires the user to deal with excessive complexity.

This is my model:
For each attack listed, provide a corresponding number weighting it.

4 Slash
2 Rock
1 Fortify
2 Cure

This creates a system where

Slash is chosen 4/9 times
Rock is chosen 2/9 times
Fortify is chosen 1/9 times
Cure is chosen 2/9 times

The actual mathematics required are simple:
1 Normalize the weights to a fixed range (eg 0..256)
2 Pick a random number inside that range, inclusive
3 Select the box that that number falls into (eg. Slash, Rock, Fortify)

Example code (in Python):

def weightedSample (seq, weights):
"""Randomly choose an element from seq according to weights. example:
seq = ("Red","Green","Blue")
weights = ( 20, 10, 5)
weightedSample(seq, weights)

returns "Red" ~20/35 of the time, "Green" ~10/35 of the time, and "Blue" ~5/35 of the time.

Sorting the choices so the most likely come first will speed things up.
"""

\#calculate total
total = sum(weights)

\#elect a partition
n = randint (1,total)
for i in range (len (weights)):
    n -= weights[i]
    if n <= 0:
        return seq[i]

return None

A more useful thing to implement is reliable probabilities, like this routine does; it could be used to enforce enemy battle behaviour:

def boxfill (seq, status, weights = None):
"""Fill weighted boxes in a roughly even way.

status is used to inversely weight weights. status is a sequence of matching length to seq.
Weighting is ((1.0/statusval) \* weight)
Thus, the more times something has been chosen relative to its siblings, 
the less likely it is to be chosen.

As a choice falls behind the others in amount-of-times-chosen, its likelihood to be chosen
approaches infinity (but is never actually infinite. 
for instance, if choice A has been picked 4million times and choice B has been picked 10 times, there
is a 1 / 400001 chance of A being picked next.

Returns an element from seq; Updates status in-place.
"""
if weights:
    _w = [(1.0 / s) \* w for s,w in zip (status, weights)]
else:
    _w = [1.0 / s for s in status]
total = sum(_w)

\#elect a partition
n = randint (1,total)
for i in range (len (weights)):
    n -= _w[i]
    if n <= 0:
        status[index] += 1
        return seq[i]
return None

@ohrrpgce-bugbot
Copy link
Author

Comment author: @pkmnfrk

Assuming we're going with the weighting system, I envision code like this:

function RandomAttack(attack() as AttackSlot) as integer
dim expAttacks() as integer
dim numslots = 0

for i = 0 to 4 'iterate over each attack slot
if attack(i).weight > 0 then
redim preserve expAttacks(numSlots + attack(i).weight - 1)

for j = numSlots to numSlots + attack(i).weight - 1
expAttacks(j) = attack(i).id
next
numslots += attack(i).weight
end if
next

if numslots = 0 then exit sub

'now the array looks like (0,0,0,0,1,1,2)

i = int(rnd * numSlots)
return expAttacks(i)
end function

This is assuming that enemies have an AttackSlot UDT with weight and ID fields to hold these datas.

I will look into making this reality this weekend.

@ohrrpgce-bugbot
Copy link
Author

Comment author: @rversteegen

That's a very strange implementation. Why not just go with Neo's sensible/obvious algorithm?

I suppose reliable probabilities would be nice, but they need not necessarily have that strong of a feedback (eg 2 / (2 + statusval) * weight)

Weren't there plans for more intelligent/programmable enemies? Ones with more than just 3 sets of attacks?

@ohrrpgce-bugbot
Copy link
Author

Comment author: @pkmnfrk

Actually, I didn't look close enough at the first part, and that seems pretty good. As for the second part, while I understand what he's doing, I don't understand Python enough to know how he's doing it. Plus, I'd prefer simple random attacks (i.e., there's always a 1/9 chance of it using Fortify), rather than guaranteeing that every 9 turns, at some point, it'll have used Fortify.

Yes, I understand that this behaviour might be useful in more than one circumstance, but battle scripting or the "more intelligent monsters" would be a better way of going about this (attacks that are used ever X turns, etc)

@ohrrpgce-bugbot
Copy link
Author

Comment author: @rversteegen

Yes, I understand that this behaviour might be useful in more than one
circumstance, but battle scripting or the "more intelligent monsters" would be
a better way of going about this (attacks that are used ever X turns, etc)

We agree there. Things should be modifiable all at once in large ways, instead of be tweaking this general bitset and that enemy bitset and some trick with elementals.

@ohrrpgce-bugbot
Copy link
Author

Comment author: @bob-the-hamster

I like these ideas, but before we jump into an implementation, there is a related consideration I want to bring up.

Right now there are three classes of enemy ai triggers.

* Normal
* Weak
* Alone

Right now each of those has five possible attacks.

In the future we may want to add additional optional AI classes, for example:

* Ally Needs Health
* Ally is poisoned
* Foe has known weakness to Fire
* Last attack damaged < 1% of target health

So when implementing attack AI weights, bear in mind that the existing attacks should be broken up into three AI classes, and we will want to re-use the same weighting code later for selecting attacks from other customizable AI classes later.

@ohrrpgce-bugbot
Copy link
Author

Comment author: @bob-the-hamster

Oh yeah. One more thing to think about, is that we may want to re-use the same code yet again at some point to give heroes (optional) AI data, for non-user-controlled heroes or heroes with "confuse" status.

@ohrrpgce-bugbot
Copy link
Author

Comment author: @pkmnfrk

What I was thinking was something like this (a kinda proto-scripting):

An enemy has a variable number of attack lists (or, a larger, fixed number of lists), and each list has an "AI" tag attached (certain ones which, when used, become unavailable in subsequent lists). The tag determines when the list is used.

The tags could be specific like "Ally needs health", or more general, like "Hero X is present", and X is customizable.

Heroes could have the same data. In fact, if we were clever enough, we could store all the lists in a "attacks.lst" lump, and have heroes and enemies both store pointers to them.

Also, we would need to add priority levels to the lists, since it would be entirely possible to have two conflicting lists, like "Enemy X is present" and "Low health". Does it save itself, or act on Enemy X? Maybe, it's a bit random, and the relative priorities of the two lists determine the odds of doing either.

Example, "Enemy X is present" has a priority of 1 (low), and "Low health" is 3. If both situations were possible, then it could:

  1. Heal itself until its health is no longer low, or,
  2. 25% of the time, do something with X, and 75% of the time heal itself.

That sort of thing.

@ohrrpgce-bugbot ohrrpgce-bugbot added attacks new feature Something new that we would like to add labels Mar 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
attacks new feature Something new that we would like to add
Projects
None yet
Development

No branches or pull requests

1 participant