Package schlachtfeld :: Module schlachtfeld
[hide private]
[frames] | no frames]

Source Code for Module schlachtfeld.schlachtfeld

  1  #!/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  # Schlachtfeld - Großk�mpfe im EWS System  
  5  #   http://rpg-tools-1d6.sf.net 
  6  # Copyright © 2007 - 2007 Achim Zien 
  7   
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12   
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17   
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 
 21  # MA 02110-1301 USA 
 22   
 23  """Schlachtensimulation nach den Regeln des Ein Würfel Systems 
 24   
 25  """ 
 26   
 27   
 28  #### IMPORTS #### 
 29   
 30  # for the fighting 
 31  from random import randrange as rnd 
 32  from random import shuffle 
 33  import math 
 34  # For selection of armies through command line arguments:  
 35  import sys 
 36  # EWS standard dice 
 37  from ews import pmw6 
 38  # EWS One Roll Fight System 
 39  from ews.fight import fight 
 40  # Name-Generator 
 41  import namen 
 42  # Obtain Charakters from yaml-files 
 43  from amov import Charakter, Versionsverwaltung 
 44  # replaced the alias with the real directory.  
 45  from Group import * 
 46  from Char import * 
 47   
 48   
 49  #### IMPORTS #### 
 50   
 51   
 52   
 53   
 54  #### CONTROL PARAMETERS #### 
 55   
 56  # BIAS 
 57   
 58  # each factor of biasfactor gives +1 to attack for superior numbers 
 59  biasfactor = 1.4 
 60   
 61  # multiplier for the bias calculation 
 62  biasmultiplier = 2.0 
 63   
 64  # the maximum biasfactor (if multiplied with biasmultiplier, approached asymptotically 
 65  biascutoff = 8 
 66   
 67   
 68  ## MAYBE STUFF FROM HERE ON DOWNWARDS COULD BE ARMY / RACE DEPENDENT ## 
 69   
 70  # initial exp gain, to be modified by opponents and own curent exp 
 71  expfactor = 1 # disabled 
 72   
 73  # factor by which the exp of the char goes into diminishing the exp gain 
 74  exp_logfactor = 1.5 
 75   
 76   
 77  # POWERRATIO 
 78   
 79  # weighting of inactive fighters 
 80  inactvalue = 0.7 
 81   
 82  # weighting of wounded fighters 
 83  woundvalue = 0.4 # obsolete 
 84   
 85  # minimum absolute remaining fraction of fighters until resign/flee 
 86  powervalue = 0.2 
 87   
 88  #### CONTROL PARAMETERS #### 
 89   
 90   
 91   
 92   
 93  #### Top-Level Objects #### 
 94   
 95  name_objekt = namen.Name() 
 96  verbose = True 
 97   
 98  #### Top-Level Objects #### 
 99   
100   
101   
102   
103  #### WRAPPERS #### 
104   
105 -def ews():
106 """Do one roll with the plusminus d6 die (wrapper for a function in pmw6).""" 107 return pmw6.pmw6()
108
109 -def check(skill,MW):
110 """Check if a given skill test reached a min-value (wrapper for a function in pmw6).""" 111 return pmw6.check(skill,MW)
112
113 -def ocheck(skill,MW):
114 """Return the degree of success / failure for a skill check (wrapper for a function in pmw6).""" 115 return pmw6.ocheck(skill,MW)
116 117 #### WRAPPERS #### 118 119 120 121 122 #### CLASSES #### 123
124 -class BattleField:
125
126 - def __init__(self,armies):
127 ''' 128 Create a new battle with armies, groups, leaders, soldiers and characters. Contains of several consecutive battle-rounds. 129 armies should be a list of combattants (usually 2) containing in a list: 130 1. their army settings - (name,resignratio,fleeratio) 131 2. a list of settings for the contained groups - (name,resignratio,fleeratio) 132 3. a dictionary containing the template-nameswith a tuple containing template-switch and a list of corresponding tuples (number of soldiers, index number of host(0 is army),chartype) - {'template_name_1':(True, [(number_count_1a,host#1a,Typus1a),(number_count_1b,host#1b,Typus1b)]), 'template_name_2':(False,[(number_count_2,host#2,Typus2)]), ...} 133 ''' 134 self.output = "" #: The output of the battlefield calculations for use in other programs. 135 if verbose: 136 output = "Pitching the following armies against each other: " + str(armies) 137 print output 138 self.output += output 139 self.Armies = [] 140 self.Groups = [] 141 self.Soldiers = [] 142 self.Leaders = [] 143 self.Heroes = [] 144 for i in range(len(armies)): 145 self.Armies.append(Army(armies[i][0][0],armies[i][0][1],armies[i][0][2])) 146 self.Armies[-1].setbattlefield(self) 147 for j in range(len(armies[i][1])): 148 self.Groups.append(Group(armies[i][1][j][0],armies[i][1][j][1],armies[i][1][j][2])) 149 self.Groups[-1].sethhost(self.Armies[i]) 150 self.Groups[-1].setbattlefield(self) 151 self.Armies[-1].groups.append(self.Groups[-1]) 152 for j in armies[i][2]: 153 if j[3] == 'Soldat': 154 concern = self.Soldiers 155 Model = Char 156 elif j[3] == 'Anführer': 157 concern = self.Leaders 158 Model = Leader 159 elif j[3] == 'Held': 160 concern = self.Heroes 161 Model = Hero 162 else: 163 raise UnmatchedChartype 164 for k in range(j[1]): 165 spawn = Model(source=j[0],template=j[4]) 166 if j[2] == -1: 167 spawn.sethost(self.Armies[-1]) 168 else: 169 spawn.sethost(self.Armies[-1].groups[j[2]]) 170 concern.append(spawn) 171 spawn.host.active.append(spawn) 172 173 #guy = armies[i][2][j] 174 #for k in range(len(guy[1])): 175 #tmp = Char(source=j,template=guy[0]) 176 #chartype = guy 177 #if tmp.chartype == 'Soldat': 178 #concern = self.Soldiers 179 #elif tmp.chartype == 'Anführer': 180 #concern = self.Leaders 181 #elif tmp.chartype == 'Held': 182 #concern = self.Heroes 183 #else: 184 #raise UnmatchedChartype 185 186 #tmp.sethost(self.Armies[guy[1][k][1]]) 187 #length = len(concern) 188 #concern.append([tmp.copy() for l in range(guy[1][k][2])]) 189 #for l in range(guy[k][2]): 190 #concern[length+l].name_me() 191 #for i in self.Soldiers + self.Heroes + self.Leaders: 192 #i.name_me() 193 self.Pairs = [] 194 self.gPairs = [] 195 self.fGroups = self.Armies + self.Groups 196 print "fGrous:", self.fGroups 197 return
198 199
200 - def battle(self):
201 ''' 202 Execute a single round of the battle together with organization of Armies, Groups and Characters. 203 Performs the following steps: 204 1. Enemy finding for single groups 205 2. Characters prepare themselves 206 3. Groups prepare themselves 207 4. Pairs of groups evaluate their combat modifiers 208 5. Pairs of soldiers are formed 209 6. They fight each other 210 ''' 211 print self.Armies 212 self.auto_enemy() 213 print "\ngPairs:", self.gPairs 214 self.char_preparations() 215 self.group_preparations() 216 for i in self.gPairs: 217 self.group_pairings(i) 218 self.char_pairings(i) 219 self.fight_rows() 220 return
221 222
223 - def auto_enemy(self):
224 '''Automatically assign enemies to groups / armies.''' 225 shuffle(self.fGroups) 226 if verbose: 227 print "\nCreating pairs from the following groups:", self.fGroups, "\n" 228 for i in range(len(self.fGroups)): 229 if len(self.fGroups) == 0: 230 print "fGroups is empty." 231 break 232 elif len(self.fGroups) == 1: 233 print "fGroups has only one member." 234 break 235 else: 236 print "\nGroup", i 237 guy = self.fGroups.pop() 238 if verbose: 239 print "Group 0:", guy 240 if i == len(self.fGroups): 241 if verbose: 242 print "no groups left to pair with each other." 243 break 244 # if guy.empty: 245 #print "\nGuy.empty:", guy.empty 246 #print "No group to pair: ", guy 247 #continue 248 cnt = -1 249 while cnt == -1:# ToDo: Add guy.host # or guy.enemy.host == guy.host: 250 print "cnt:", cnt, "len(self.fGroups):", len(self.fGroups) 251 guy.enemy = self.fGroups[cnt] 252 cnt -= 1 253 if cnt + len(self.fGroups) == 0: # breaks at end/beginning of list 254 break 255 if guy.enemy: 256 if verbose: 257 print "paired with:", guy.enemy, "." 258 guy.enemy.enemy = guy 259 if (0 - cnt - 1) != len(self.fGroups): 260 self.fGroups = self.fGroups[:cnt-1] + self.fGroups[cnt+1:] 261 else: # Just cut off the first member of fGroups. 262 self.fGroups = self.fGroups[cnt+1:] 263 self.gPairs.append((guy, guy.enemy)) 264 return
265 266
267 - def char_preparations(self):
268 ''' 269 Make all the characters still involved do their preparations: 270 Leader(): .plan, .speak and .checkmorale 271 Hero(): .motivate and .checkmorale 272 Char(): .checkmorale 273 ''' 274 for i in self.Leaders: 275 if i.alive and not i.queue and not i.fled and i.host: 276 i.plan() 277 i.speak() 278 i.checkmorale() # leaders get the hero morale bonus 1 round delayed - which is ok for me since they are not within the heap of soldiers 279 for i in self.Heroes: 280 if i.alive and not i.queue and not i.fled and i.host: 281 i.motivate() 282 i.checkmorale() 283 for i in self.Soldiers: 284 if i.alive and not i.queue and not i.fled and i.host: 285 i.checkmorale() 286 return
287 288
289 - def group_preparations(self):
290 ''' 291 Make all non-empty groups do their preparations for battle: 292 1. find enemies 293 2. count numbers 294 ''' 295 for i in self.Armies + self.Groups: 296 i.count() 297 i.fleeresigncheck() 298 #shuffle(i.active) # not consistent with battle lines! 299 return
300 301
302 - def group_pairings(self,groups):
303 '''Take selected pairs of groups to calculate their powerratio/aliveratio and evaluate their relative bias.''' 304 powercheck(groups) 305 bias_me(groups) 306 return
307 308
309 - def char_pairings(self,groups):
310 ''' 311 Pair the Characters from two Groups to fight each other. Only the Characters from the front line are taken into account. 312 groups: should be a tuple of two Group() or Army() instances. 313 ''' 314 self.Pairs = [] 315 n = min([i.line for i in groups]) 316 if verbose: 317 print "\nPairs: n =", n 318 for i in range(n): 319 self.Pairs.append([j.active[i] for j in groups]) # ToDo: This sometimes returns a "list index out of range" error. 320 return
321 322
323 - def fight_rows(self):
324 for i in self.Pairs: 325 fight(i) 326 experience(i)
327 328
329 - def __repr__(self):
330 '''Nice printout of current situation on the BattleField().''' 331 scribble = 'Yeah, the proper printout is still lacking' 332 return scribble
333 334
335 - def auto_battle(self,infolevel=0):
336 '''Automatically executes battles plus intermediate output until the battle ends (flight, extermination, resignation).''' 337 return
338 339
340 - def supplies(self):
341 '''Give one of the Armies/Groups additional support in form of new soldiers, leaders, heroes or entire groups.''' 342 # wrapper for Group().method? 343 return
344 345 346 #### CLASSES #### 347 348 349 350 351 #### FUNCTIONS #### 352 353
354 -def powercheck(Groups):
355 """Compare the ratio between fighters who are alive in each group (but not necessarily able to fight).""" 356 for i in Groups: 357 i.aliveratio = float(i.nact + i.ninact * inactvalue) / max(1, i.n) 358 for i in range(len(Groups)): 359 # compare the ratio of fighters who are able to fight on each side. 360 if Groups[1-i].aliveratio != 0: 361 Groups[i].powerratio = Groups[i].aliveratio / Groups[1-i].aliveratio 362 # if the other army has no fighters left, just set it to 1. 363 else: 364 Groups[i].powerratio = 1
365 366
367 -def bias_me(armies):
368 '''Calculate the bias between two groups and store it in Grou().situation[1].''' 369 if max(armies[0].nact, armies[1].nact) < 5: 370 if armies[0].nact > armies[1].nact: 371 bias = int(round(3 * float(armies[0].nact - armies[1].nact)/max(1, armies[1].nact))) 372 else: 373 bias = 3 * float(armies[1].nact - armies[0].nact)/max(1, armies[0].nact) 374 else: 375 q = math.log(float(max(1, armies[0].nact))/max(1, armies[1].nact),biasfactor) 376 bias = int(round(biasmultiplier * q/(1+abs(q)/biascutoff))) 377 armies[0].situation[1] = -bias 378 armies[1].situation[1] = bias
379 380 381 #### FUNCTIONS #### 382
383 -def experience(chars):
384 # experience 385 for j in range(2): 386 expadd = max(math.log(max(chars[1-j].exp, 1), exp_logfactor), 1) / max(chars[j].exp, 3) # * expfactor 387 chars[j].upgrade(expadd) 388 chars[j].escape_check() 389 if verbose: 390 print '\n', chars[j] 391 if verbose: 392 print '\n\n' 393 394 #### SELF-CHECK #### 395 396 if __name__ == '__main__': 397 398 ## get command line pars 399 ## 1st 400 #try: 401 #choice_of_army = sys.argv[1] 402 #except: 403 #choice_of_army = 'default' 404 ## 2nd 405 #try: 406 #if sys.argv[2] == "verbose": 407 #print "\nBereite das Schlachtfeld vor..." 408 #verbose = True 409 #except: 410 #verbose = False 411 412 ## the (preliminary) army configurations 413 #if choice_of_army == "unbalanced": 414 #Armies = [Group(1000, exp=0, weap=4, arm=2, name=u'Average Army'), 415 #Group(1000, exp=3, weap=4, arm=2, name=u'Veteran Army')] 416 417 ## This is the equivalent of a newbie army with heavy axes and chainmail against an experienced army with rapiers and thin leather mail. 418 #elif choice_of_army == "balanced": 419 #Armies = [Group(1000, exp=0, weap=5, arm=3, name=u'Rich Average Army'), 420 #Group(1000, exp=3, weap=3, arm=2, name=u'Poor Veteran Army')] 421 422 ## Noch eine extrem disbalancierte Armeenkonstellation: 423 #elif choice_of_army == "Menschen_gegen_Monster": 424 #Armies = [Group(60, exp=0, weap=4, arm=2, name=u'Menschen'), 425 #Group(26, exp=6, weap=4, arm=2, name=u'Monster')] # => Fert ~ 18, TP ~ 26, WS ~ 6 426 427 ## Eine noch extremer disbalancierte Armeenkonstellation: 428 #elif choice_of_army == "Menschen_gegen_Legenden": 429 #Armies = [Group(60, exp=0, weap=4, arm=2, name=u'Menschen'), 430 #Group(10, exp=12, weap=4, arm=2, name=u'Legenden')] # => Fert ~ 24, TP ~48, WS ~ 8 431 432 ## Und noch schlimmer (Eine Kategorie unterschied): 433 #elif choice_of_army == "Menschen_gegen_Halbgoetter": 434 #Armies = [Group(180, exp=0, weap=4, arm=2, name=u'Menschen'), 435 #Group(3, exp=24, weap=4, arm=2, name=u'Halbgötter')] # => Fert ~ 36, TP ~72, WS ~ 12 436 437 ## Und schlussendlich (Kategorie 4 (Menschen) gegen Kategorie 6 (Riesen und größere): 438 #elif choice_of_army == "Menschen_gegen_Riesen": 439 #Armies = [Group(10000, exp=0, weap=4, arm=2, name=u'Menschen'), 440 #Group(2, exp=48, weap=4, arm=2, name=u'Riesen')] # => Fert ~ 60, TP ~120, WS ~ 20 441 442 ## Als interessante Versuch fügen wir 4 Menschen gegen eine Leeegnnd hizu: 443 #elif choice_of_army == "Vier_Menschen_gegen_eine_Legende": 444 #Armies = [Group(4, exp=0, weap=4, arm=2, name=u'Menschen'), 445 #Group(1, exp=12, weap=4, arm=2, name=u'Legende')] # => Fert ~ 24, TP ~48, WS ~ 8 446 447 ## Als interessante Versuch fügen wir 4 Menschen gegen eine Leeegnnd hizu: 448 #elif choice_of_army == "Fuenf_Menschen_gegen_eine_Legende": 449 #Armies = [Group(5, exp=0, weap=4, arm=2, name=u'Menschen'), 450 #Group(1, exp=12, weap=4, arm=2, name=u'Legende')] # => Fert ~ 24, TP ~48, WS ~ 8 451 452 ## Und jetzt noch die einfache unfiare Situation: Drei gegen Zwei 453 #elif choice_of_army == "Drei_gegen_Zwei": 454 #Armies = [Group(3, exp=0, weap=4, arm=2, name=u'Gruppe von Drei'), 455 #Group(2, exp=0, weap=4, arm=2, name=u'Gruppe von Zwei')] 456 457 ## Massenschlacht 458 #elif choice_of_army == "Massenschlacht": 459 #Armies = [Group(300000, exp=0, weap=4, arm=2, name=u'Riesige Armee 1'), 460 #Group(300000, exp=0, weap=4, arm=2, name=u'Riesige Armee 2')] 461 462 ## standard: Menschen gegen Goblins, beide aus Dateien geladen 463 #else: 464 #Armies = [Group(1000, exp=0, weap=4, arm=2, name=u'Standard Armee 1', source="tag:1w6.org,2007:Mensch",leaderpars=(0,4,2,24,12,12,12,15,15,u"tag:1w6.org,2007:Mensch")), 465 #Group(1200, exp=0, weap=4, arm=2, name=u'Standard Armee 2', source="tag:1w6.org,2007:Goblin",leaderpars=(0,4,2,24,12,12,12,12,12,u"tag:1w6.org,2007:Goblin"))] 466 467 468 ## Show the Armies, if we're in a talky mood 469 #if verbose: 470 #for i in Armies: 471 #print i 472 473 ## battle now!! 474 #rounds = 0 475 #for i in range(len(Armies)): 476 #Armies[i].get_enemy(Armies[1-i]) 477 #while not battle(Armies): 478 #rounds += 1 479 #print '\n' + `rounds`, 'Runde(n)' 480 #for i in Armies: 481 #i.resumee() 482 #print '\n' 483 484 485 armies = [(('Menschen Armee',0.5,0.4), [], [("tag:1w6.org,2007:Mensch",10,-1,'Soldat',True)]), 486 (('Goblin Armee',0.4,0.4), [], [("tag:1w6.org,2007:Goblin",15,-1,'Soldat',True)])] 487 488 test = BattleField(armies) 489 for i in range(5): 490 test.battle() 491 for i in test.Armies: 492 print i 493 print '\n' 494 print '... done!' 495 496 497 #### SELF-CHECK #### 498