Implementation of abilities using generators
General Discussion of the Intricacies
Moderator: CCGHQ Admins
1 post
• Page 1 of 1
Implementation of abilities using generators
by Incantus » 14 Sep 2008, 23:23
Incantus uses python generators (which are similar to coroutines, although not quite as powerful) to implement the ability engine. Here's a good intro http://loveandtheft.org/2008/08/29/introduction-to-generators-in-python/. Generators allow you to stop and resume a function from predefined points within that function, using the 'yield' keyword.
For example, here is the code for Corrupt:
Back to generators. Basically, the yield statement allows you to yield back to the caller of the function (sort of like return), but the next time the function is called it resumes at the most recent point where it yielded. One common use of a generator is simulate an infinite sequence, without actually having to generate it all. For example, you can write an infinite counter:
So back to Incantus. When an ability/spell is played, the effect function is called, a cost is yielded back to the rules engine, then the function is called again, passing back the payment, and then the effects function yields the Target object and gets back the actual target selected. Finally, when the ability resolves, the effects function is called, resuming at the second yield call. One major benefit of this approach is that all important objects (the cost payment, the target) are local to the function and can be referred to later in the effects code.
For example, here is the code for Corrupt:
- Code: Select all
name = 'Corrupt'
cardnum = 0
expansion = ''
types = characteristic('Sorcery')
supertypes = no_characteristic()
subtypes = no_characteristic()
cost = '5B'
color = characteristic('B')
text = ['Corrupt deals damage equal to the number of Swamps you control to target creature or player. You gain life equal to the damage dealt this way.']
@play_sorcery()
def effects(controller, source):
payment = yield source.cost
target = yield Target(target_types=isCreatureOrPlayer)
num_swamps = len(controller.play.get(isLand.with_condition(lambda l: l.subtypes == "Swamp")))
dmg = source.dealDamage(target, num_swamps)
controller.life += dmg
yield
play_spell = effects
Back to generators. Basically, the yield statement allows you to yield back to the caller of the function (sort of like return), but the next time the function is called it resumes at the most recent point where it yielded. One common use of a generator is simulate an infinite sequence, without actually having to generate it all. For example, you can write an infinite counter:
- Code: Select all
def count():
i = 0
while True:
yield i
i += 1
So back to Incantus. When an ability/spell is played, the effect function is called, a cost is yielded back to the rules engine, then the function is called again, passing back the payment, and then the effects function yields the Target object and gets back the actual target selected. Finally, when the ability resolves, the effects function is called, resuming at the second yield call. One major benefit of this approach is that all important objects (the cost payment, the target) are local to the function and can be referred to later in the effects code.
1 post
• Page 1 of 1
Return to Magic Rules Engine Programming
Who is online
Users browsing this forum: No registered users and 17 guests