C# ported code


In [182]:
import random
import time

meeleHit =[
              #Oponent 1  2  3  4  5  6  7  8  9  10
                      [4, 4, 5, 5, 5, 5, 5, 5, 5, 5 ], #1
                      [3, 4, 4, 4, 5, 5, 5, 5, 5, 5 ], #2
                      [3, 3, 4, 4, 4, 4, 5, 5, 5, 5 ], #3
                      [3, 3, 3, 4, 4, 4, 4, 4, 5, 5 ], #4
                      [3, 3, 3, 3, 4, 4, 4, 4, 4, 4 ], #5
                      [3, 3, 3, 3, 3, 4, 4, 4, 4, 4 ], #6
                      [3, 3, 3, 3, 3, 3, 4, 4, 4, 4 ], #7
                      [3, 3, 3, 3, 3, 3, 3, 4, 4, 4 ], #8
                      [3, 3, 3, 3, 3, 3, 3, 3, 4, 4 ], #9
                      [3, 3, 3, 3, 3, 3, 3, 3, 3, 4 ] #10
]
wounds = [
              #Oponent 1  2  3  4  5  6  7  8  9  10
                     [ 4, 5, 6, 6, 99, 99, 99, 99, 99, 99 ], #1
                     [ 3, 4, 5, 6, 6, 99, 99, 99, 99, 99 ],  #2
                     [ 2, 3, 4, 5, 6, 6, 6, 99, 99, 99],     #3
                     [ 2, 2, 3, 4, 5, 6, 6, 99, 99, 99 ],    #4
                     [ 2, 2, 2, 3, 4, 5, 6, 6, 99, 99 ],     #5
                     [ 2, 2, 2, 2, 3, 4, 5, 6, 6, 99 ],      #6
                     [ 2, 2, 2, 2, 2, 3, 4, 5, 6, 6 ],       #7
                     [ 2, 2, 2, 2, 2, 2, 3, 4, 5, 6 ],       #8
                     [ 2, 2, 2, 2, 2, 2, 2, 3, 4, 5 ],       #9
                     [ 2, 2, 2, 2, 2, 2, 2, 2, 3, 4 ]        #10
]

class Dice(object):
    def once(self):
        return random.randint(1, 6)

    def rollSpecial(self):
        value = self.once()

        if value == 6:
            new_value = self.once()
            if (new_value == 6): return 9
            elif (new_value == 5): return 8
            elif (new_value == 4): return 7
            
        return value

ALIVE = 0
DEAD = 1
    
class Unit(object):
    def __init__(self, attributes, name, verbose=True, timeMultiplier=1000.0):
        self.dice = Dice()
        self.name = name
        self.attributes = attributes.copy()
        self.wounds = 0
        self.status = ALIVE
        self.lastAttack = time.time()
        self.verbose = verbose
        
        self.attributes['attackRate'] *= timeMultiplier
        
    def willAttackLand(self, frm, isRanged = False):
        dice = self.dice.rollSpecial()
   
        if isRanged:
            projectileAbility = frm.attributes['projectileAbility'];
                   # + from.accumulatedModifier<UnitAbility>().projectileAbilityModifier +
                   # from.computeRangedModifiers();
   
            return dice > 1 and (projectileAbility + dice >= 7)

        attackerAbility = min(10, frm.attributes['weaponAbility'])
               # + from.accumulatedModifier<UnitAbility>().weaponAbilityModifier);
   
        defenderAbility = min(10, self.attributes['weaponAbility'])
               # accumulatedModifier<UnitAbility>().weaponAbilityModifier);
        
        return meeleHit[attackerAbility - 1][defenderAbility - 1] <= dice
    
    def willAttackCauseWounds(self, frm):
        attackerStrength = min(10, frm.attributes['strength'])
        # + from.accumulatedModifier<UnitAbility>().weaponAbilityModifier);
   
        defenderResistance = min(10, self.attributes['resistance'])
        # + accumulatedModifier<UnitAbility>().resistanceModifier);
   
        dice = self.dice.once()
    
        return wounds[attackerStrength - 1][defenderResistance - 1] <= dice
    
    def receiveAttack(self, frm, isRanged):
        hitAndWounds = self.willAttackLand(frm, isRanged) and self.willAttackCauseWounds(frm);
        if hitAndWounds:
            self.wounds += 1

        if self.wounds == self.attributes['wounds']:
            self.status = DEAD
            if self.verbose: print "HEY! I'M DEAD!", self.name
            

    def attackTarget(self, to):
        # (Time.time - _lastAttack >= (1f / info.unitAttributes.attackRate)))
        self.target = to
        
        each_attack = 1.0 / self.attributes['attackRate']
        while time.time() - self.lastAttack >= each_attack:
            if self.verbose: print ".",
            to.receiveAttack(self, False)            
            self.lastAttack += each_attack



Test environment


In [193]:
import numpy as np

def wrap_tests(MAX_ITER, generator, verbose=False):
    results = None

    for i in range(1, MAX_ITER + 1):
        stopCriteria, attacks = generator(i)
        
        current_results = test(stopCriteria, attacks, verbose = verbose or (i == MAX_ITER))
        
        if results is None:
            results = current_results
        else:
            results += current_results
    
    return results

def test(stopCriteria, attacks, verbose=True):
    results = np.zeros(len(stopCriteria))
    
    while True:
        stop = False
        
        for k,criteria in enumerate(stopCriteria):
            dead = 0
            for u in criteria:
                dead += int(u.status == DEAD)
                
            stop = (dead == len(criteria))
            results[k] += stop
            
            if stop:
                break
        
        if stop:
            break
        
        for group in attacks:
            attacker = group[0]
            
            if attacker.status == DEAD:
                continue
            
            for attacked in group[1:]:
                if not attacked.status == DEAD:
                    attacker.attackTarget(attacked)
                    break

    if verbose: 
        printed = set()

        for group in stopCriteria:
            for unit in group:
                if unit not in printed:
                    print "%s is %s, %d / %d" % (unit.name, "DEAD" if unit.status == DEAD else "ALIVE", unit.wounds, unit.attributes['wounds'])
                    printed.add(unit)
            print
            
    return 1 - results

In [214]:
lightAttrs = {
        "weaponAbility": 3,
        "projectileAbility": 0,
        "strength": 3,
        "resistance": 4,
        "wounds": 15,

        "attackRate": 2,
        "movementRate": 2.4,
        "sightRange": 40,
        "creationTime": 25,

        "foodConsumption": 0.3,
    }

humanLightAttrs = {
        "weaponAbility": 2,
        "projectileAbility": 0,
        "strength": 4,
        "resistance": 4,
        "wounds": 15,

        "attackRate": 1.84,
        "movementRate": 2.4,
        "sightRange": 40,
        "creationTime": 25,

        "foodConsumption": 0.3,
    }

heavyAttrs = {
        "weaponAbility": 5,
        "projectileAbility": 0,
        "strength": 5,
        "resistance": 5,
        "wounds": 25,

        "attackRate": 1.7,
        "movementRate": 1.8,
        "sightRange": 40,
        "creationTime": 50,

        "foodConsumption": 0.8,
    }

humanHeavyAttrs = {
        "weaponAbility": 4,
        "projectileAbility": 0,
        "strength": 6,
        "resistance": 5,
        "wounds": 27,

        "attackRate": 1.564,
        "movementRate": 1.8,
        "sightRange": 40,
        "creationTime": 50,

        "foodConsumption": 0.8,
    }

knightAttrs = {
        "weaponAbility": 5,
        "projectileAbility": 4,
        "strength": 4,
        "resistance": 4,
        "wounds": 15,

        "attackRate": 3,
        "movementRate": 4,
        "sightRange": 40,
        "creationTime": 8
    }

heroAttrs =  {
        "weaponAbility": 7,
        "projectileAbility": 0,
        "strength": 9,
        "resistance": 7,
        "wounds": 30,

        "attackRate": 3,
        "movementRate": 2.4,
        "sightRange": 50,
        "creationTime": 30
    }

entAttrs = {
        "weaponAbility": 6,
        "projectileAbility": 0,
        "strength": 5,
        "resistance": 8,
        "wounds": 70,

        "attackRate": 1,
        "movementRate": 3,
        "sightRange": 12,
        "creationTime": 12
    }

Test: 2 lights vs 1 heavy


In [202]:
MAX_ITER = 100
def generator(i):
    light1 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    light2 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    #light3 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    heavy1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))

    #stopCriteria = ((light1, light2, light3), (heavy1,))
    #attacks = ((light1, heavy1), (light2, heavy1), (light3, heavy1), (heavy1, light1, light2, light3))
    
    stopCriteria = ((light1, light2), (heavy1,))
    attacks = ((light1, heavy1), (light2, heavy1), (heavy1, light1, light2))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
light is DEAD, 15 / 15
light is DEAD, 15 / 15

heavy is ALIVE, 9 / 25

Out[202]:
array([   0.,  100.])

Test: 3 lights vs 1 heavy


In [216]:
MAX_ITER = 100
def generator(i):
    light1 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    light2 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    light3 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    heavy1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))

    stopCriteria = ((light1, light2, light3), (heavy1,))
    attacks = ((light1, heavy1), (light2, heavy1), (light3, heavy1), (heavy1, light1, light2, light3))
    
    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
light is DEAD, 15 / 15
light is DEAD, 16 / 15
light is DEAD, 15 / 15

heavy is ALIVE, 17 / 25

Out[216]:
array([ 20.,  80.])

Test: 2 lights vs 1 knight (cavalry)


In [151]:
MAX_ITER = 100
def generator(i):
    light1 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    light2 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    knight1 = Unit(knightAttrs, 'heavy', verbose = (i == MAX_ITER))
    
    stopCriteria = ((light1, light2), (knight1,))
    attacks = ((light1, knight1), (light2, knight1), (knight1, light1, light2))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. HEY! I'M DEAD! heavy
light is DEAD, 15 / 15
light is ALIVE, 14 / 15

heavy is DEAD, 15 / 15

Out[151]:
array([ 95.,   5.])

Test: 1 lights vs 1 knight


In [152]:
MAX_ITER = 100
def generator(i):
    light1 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    knight1 = Unit(knightAttrs, 'knight', verbose = (i == MAX_ITER))
    
    stopCriteria = ((light1,), (knight1,))
    attacks = ((light1, knight1), (knight1, light1))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
. HEY! I'M DEAD! light
. HEY! I'M DEAD! light
light is DEAD, 15 / 15

knight is ALIVE, 14 / 15

Out[152]:
array([   0.,  100.])

Test: 1 light vs 1 light Human


In [204]:
MAX_ITER = 200
def generator(i):
    light1 = Unit(lightAttrs, 'light', verbose = (i == MAX_ITER))
    lightH = Unit(humanLightAttrs, 'light_human', verbose = (i == MAX_ITER))
    
    stopCriteria = ((light1,), (lightH,))
    attacks = ((light1, lightH), (lightH, light1))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! light
light is DEAD, 15 / 15

light_human is ALIVE, 9 / 15

Out[204]:
array([  89.,  111.])

Test: 1 heavy vs 1 heavy Human


In [213]:
MAX_ITER = 200
def generator(i):
    heavy1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavyH = Unit(humanHeavyAttrs, 'heavy_human', verbose = (i == MAX_ITER))
    
    stopCriteria = ((heavy1,), (heavyH,))
    attacks = ((heavy1, heavyH), (heavyH, heavy1))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. heavy is DEAD, 26 / 25

heavy_human is ALIVE, 18 / 27

Out[213]:
array([  97.,  103.])

Test: 1 heavy vs 1 knight


In [178]:
MAX_ITER = 100
def generator(i):
    heavy1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    knight1 = Unit(knightAttrs, 'knight', verbose = (i == MAX_ITER))
    
    stopCriteria = ((heavy1,), (knight1,))
    attacks = ((heavy1, knight1), (knight1, heavy1))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! knight
. HEY! I'M DEAD! knight
heavy is ALIVE, 15 / 25

knight is DEAD, 15 / 15

Out[178]:
array([ 100.,    0.])

Test: 4 heavies vs 1 hero


In [215]:
MAX_ITER = 100
def generator(i):
    heavy1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavy2 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavy3 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavy4 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    hero1 = Unit(heroAttrs, 'hero', verbose = (i == MAX_ITER))

    #stopCriteria = ((light1, light2, light3), (heavy1,))
    #attacks = ((light1, heavy1), (light2, heavy1), (light3, heavy1), (heavy1, light1, light2, light3))
    
    stopCriteria = ((heavy1, heavy2, heavy3, heavy4), (hero1,))
    attacks = ((heavy1, hero1), (heavy2, hero1), (heavy3, hero1), (heavy4, hero1), (hero1, heavy1, heavy2, heavy3, heavy4))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. HEY! I'M DEAD! heavy
. HEY! I'M DEAD! heavy
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. . heavy is DEAD, 26 / 25
heavy is DEAD, 25 / 25
heavy is DEAD, 25 / 25
heavy is DEAD, 26 / 25

hero is ALIVE, 27 / 30

Out[215]:
array([  4.,  96.])

Test: Ent + sth vs a lot of sth!


In [185]:
MAX_ITER = 100
def generator(i):
    heavy1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavy2 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavy3 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    heavy4 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    
    ent1 = Unit(entAttrs, 'ent', verbose = (i == MAX_ITER))
    heavy_e1 = Unit(heavyAttrs, 'heavy', verbose = (i == MAX_ITER))
    

    stopCriteria = ((heavy1, heavy2, heavy3), (ent1,heavy_e1))
    attacks = ((heavy1, ent1,heavy_e1), (heavy2, ent1,heavy_e1), (heavy3, ent1,heavy_e1), (ent1, heavy1, heavy2, heavy3), (heavy_e1, heavy1, heavy2, heavy3))
    
    #stopCriteria = ((heavy1, heavy2, heavy3, heavy4), (hero1,))
    #attacks = ((heavy1, hero1), (heavy2, hero1), (heavy3, hero1), (heavy4, hero1), (hero1, heavy1, heavy2, heavy3, heavy4))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! heavy
heavy is DEAD, 25 / 25
heavy is DEAD, 25 / 25
heavy is DEAD, 25 / 25

ent is ALIVE, 26 / 70
heavy is ALIVE, 0 / 25

Out[185]:
array([   0.,  100.])

In [187]:
MAX_ITER = 100
def generator(i):
    hero = Unit(heroAttrs, 'hero', verbose = (i == MAX_ITER))    
    ent1 = Unit(entAttrs, 'ent', verbose = (i == MAX_ITER))
    
    stopCriteria = ((hero,), (ent1,))
    attacks = ((hero, ent1), (ent1, hero))
    
    #stopCriteria = ((heavy1, heavy2, heavy3, heavy4), (hero1,))
    #attacks = ((heavy1, hero1), (heavy2, hero1), (heavy3, hero1), (heavy4, hero1), (hero1, heavy1, heavy2, heavy3, heavy4))

    return stopCriteria, attacks

wrap_tests(MAX_ITER, generator)


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HEY! I'M DEAD! ent
. hero is ALIVE, 7 / 30

ent is DEAD, 71 / 70

Out[187]:
array([ 100.,    0.])

In [ ]: