# Copyright 2010 Perseid. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted.
#
# THIS SOFTWARE IS PROVIDED BY Perseid ``AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
def tupleAdd(a,b):
return tuple(map(lambda x:x[0]+x[1],zip(a,b)))
def armyLoss(aggressor, defender):
aggressor = sorted(aggressor)
defender = sorted(defender)
if len(defender) == 1:
if defender[0] >= aggressor[2]:
return (1,0)
else:
return (0,1)
else:
result = (0,0)
if defender[1] >= aggressor[2]:
result = tupleAdd(result,(1,0))
else:
result = tupleAdd(result,(0,1))
if defender[0] >= aggressor[1]:
result = tupleAdd(result,(1,0))
else:
result = tupleAdd(result,(0,1))
return result
def crossProduct(setList):
result = [tuple()]
for mySet in setList:
newResult = []
for x in result:
newResult.extend(map(lambda y:x+(y,),mySet))
result = newResult
return result
#for (aggr,defe) in crossProduct([crossProduct([range(1,7),range(1,7),range(1,7)]), crossProduct([range(1,7),range(1,7)])]):
#print((aggr,defe))
#print(armyLoss(aggr,defe))
partialAggrDices = sorted(list(set(
map(lambda x:tuple(sorted(x)) ,crossProduct([range(1,7),range(1,7)]))
)))
#2 Dice
TwoDiceAverage=dict()
for aggr in partialAggrDices:
fullDefeDices = crossProduct([range(1,7),range(1,7)])
results = map(lambda defe:armyLoss((1,)+aggr,defe),fullDefeDices)
sum = (0,0)
for result in results:
sum = tupleAdd(sum,result)
average = (float(sum[0])/len(fullDefeDices), float(sum[1])/len(fullDefeDices))
#print (aggr,average)
TwoDiceAverage[aggr] = average
#1 Dice
OneDiceAverage=dict()
for aggr in partialAggrDices:
fullDefeDices = [[i] for i in range(1,7)]
results = map(lambda defe:armyLoss((1,)+aggr,defe),fullDefeDices)
sum = (0,0)
for result in results:
sum = tupleAdd(sum,result)
average = (float(sum[0])/len(fullDefeDices), float(sum[1])/len(fullDefeDices))
#print (aggr,average)
OneDiceAverage[aggr] = average
#print("\n\n")
def bestStratArmyLoss(aggr):
global OneDiceAverage
global TwoDiceAverage
aggr = tuple(sorted(aggr))
aggr = (aggr[1],aggr[2])
(a,d) = OneDiceAverage[aggr]
OneDiceDiff = a-d
(a,d) = TwoDiceAverage[aggr]
TwoDiceDiff = a-d
if OneDiceDiff > TwoDiceDiff:
return OneDiceAverage[aggr]
else:
return TwoDiceAverage[aggr]
print(
"""'1 Die diff' is the average amount of armies the aggressor loses minus
the average amount of armies the defender loses, if the defender rolls
1 die.
'2 Dice diff' is the average amount of armies the aggressor loses minus
the average amount of armies the defender loses, if the defender rolls
2 dice.
A (x,y) battle loss means that the aggressor looses an average of x armies
and the defender looses an average of y armies.
""")
for aggr in partialAggrDices:
(a,d) = OneDiceAverage[aggr]
OneDiceDiff = a-d
(a,d) = TwoDiceAverage[aggr]
TwoDiceDiff = a-d
advice=""
if OneDiceDiff > TwoDiceDiff:
advice = "Use 1"
else:
advice = "Use 2"
print("Aggressor dice: {0} -> 1 Die diff: {1: .4f}; 2 Dice diff: {2: .4f} => {3}: it results in ({4[0]:.4f},{4[1]:.4f}) battle loss.".format(aggr,OneDiceDiff,TwoDiceDiff,advice,bestStratArmyLoss((1,)+aggr)))
#print("Aggressor dice:", aggr, " -> 1 Die diff:",OneDiceDiff, "; 2 Dice diff:", TwoDiceDiff," => ",advice,"; results in", bestStratArmyLoss((1,)+aggr), "battle loss")
averageBestStratLoss = (0,0)
for aggr in crossProduct([range(1,7),range(1,7),range(1,7)]):
averageBestStratLoss = tupleAdd(averageBestStratLoss,bestStratArmyLoss(aggr))
#print(averageBestStratLoss)
print("\n\nThe average army loss is {:.4f} for the aggressor and {:.4f} for the defender assuming the defender plays the ideal strategy.".format(averageBestStratLoss[0]/6.0/6.0/6.0,averageBestStratLoss[1]/6.0/6.0/6.0))