#! /usr/bin/perl
print "Content-type: text/html\n\n";
if ($ENV{"REQUEST_METHOD"} eq "POST") {
read(STDIN, $datastring, $ENV{"CONTENT_LENGTH"});
}
elsif (exists $ENV{"REQUEST_METHOD"}) { # data from GET transaction (or HEAD or other)
$datastring = $ENV{"QUERY_STRING"};
}
else {
print "Offline execution detected\n";
print "Please enter some data.\n";
$datastring = <>;
chomp $datastring;
print "== data accepted == HTML output follows ==\n\n";
}
###decode######################################################
$datastring =~s/%0D%0A/\n/g; #step to deal with line
#breaks in text areas
@nameValuePairs = split(/&/, $datastring); #step 1
foreach $pair (@nameValuePairs) {
($name, $value) = split(/=/, $pair); #step 2
$name =~tr/+/ /; #step 3
$name =~s/%([\da-fA-F]{2})/pack("C",hex($1))/eg; #step 3
$value =~tr/+/ /; #step 3
$value =~s/%([\da-fA-F]{2})/pack("C",hex($1))/eg; #step 3
if(exists $formHash{$name}) { #improved step 4,
$formHash{$name} = $formHash{$name}.";".$value; #now handles multiple
} #select menus
else {
$formHash{$name} = $value;
}
}
if($formHash{psquad} eq "squad"){$formHash{p1}=$formHash{squadp1};}
else{$formHash{p1}=$formHash{herop1};}
if($formHash{qsquad} eq "squad"){$formHash{q1}=$formHash{squadq1};}
else{$formHash{q1}=$formHash{heroq1};}
###done decoding###############################################
$note="";
$note=qq^Beta Testing Version 6.31 with Wave 13 (D3) figures.
Adding figures as they are revealed in previews.
Please report any errors or suspicious behavior.^;
$note2=qq^Do not use yet - Alpha Testing Version 6.10 with Wave 12 (D2) figures.
Not yet programmed: Psionic Blast and all the Wyrmling powers.
^;
#$note="";
$title=qq^$note
Heroscape One-on-one matchup calculator (Version 6.31, Sep 22, 2010)
Using math (not simulations), this calculates the exact win probabilities
and average length of battle in number of attacks (which is the same as number of half turns
in the absence of multiple attacks, frenzy, or squads).
The average survivors is the average number of life or figures left assuming that the
figure or squad wins.
[Back to intro page]
[About this program]
[Detailed instructions]
[To do list]
[What's new]
[Access stats]
Version 6.20: Added more choices to "Squad size" to include squads of more than 1 common heroes, which is essentially what you get when you use Wyrmling Bonding or Master of the Elements. This only makes a difference for powers that distinguish squad figures from common heros such as Chomp, Poison Acid Breath, Stare of Stone.
Version 6.11: Modified Searing Intensity.
Version 6.10: Added implementable abilities from D12: the fledgling abilities and Psionic Blast. Also fixed a typo that would have affected results of ChillingTouch and Trample and made them worse than they should be. All Chilling Touch, Throw, and Trample numbers before Aug 28, 2010 should be redone.
Version 6.00: New interface improvements, including mouseover activating balloons that describe the abilities.
Remember, it is up to the user to use this calculator wisely.
This calculator does not take into account of range or movement.
So all battles are either melee vs melee or range vs range.
A better way of thinking of it is you are assuming both sides get to use
the attacks and abilities that you give them.
You must interpret the results appropriately.
Furthermore, it doesn't make sense to matchup certain abilities.
For example, the Ullar Rifle special is only good for ranged duels, so it doesn't make sense to
match that against say melee figures.
And it is up to the user to remember that certain abilities (example: smoke powder, counterstrike, disappearing ninja, tough, etc.) work only against normal attacks.
Remember also, that in addition to using official figures, feel free to customize your own figures (within reason!).
\n^;
$maxLockFile = 150; # 2.5 minutes max for running a Mathematica calculation.
$color1="#33ff33"; $color2="#ff5550";
$gray="#d0d0d0";
$dataDir = "";
$lockfile=$dataDir."mathlock";
$mafile=$dataDir."mathinput";
$outputfile=$dataDir."mathoutput.ma";
$donefile=$dataDir."mathdone";
$datafile=$dataDir."datafile.txt";
$star=qq^ * ^;
sub mystar{ ##This will be later when we figure out how to do anchor display when returned as a CGI page
my ($anchor)=@_;
return qq^ * ^;
}
### This is an array of the web displayed names
### and names used in the Mathematica program, in pairs.
### The displayed name may be changed without affecting the Mathematica program.
$nyi=' [not done]';
sub trim{
my ($x)=@_;
$x=~s/^\s+//;
$x=~s/\s+$//;
return $x;
}
@Abilities=();
@AbilitiesMa=();
@AbilitiesDesc=();
@AbilitiesDescJS=();
%AbilityToN=();
if(open(ABIL, ";
close(ABIL);
chomp @all;chomp @all;
$all=join(" ",@all);
$all=~s/\n/ /g;
@all=split(/\#\#/, $all);
$ctr=3;
for($n=0;$n<@all;$n++){
$x=$all[$n];
if($x !~ /\-\-\-\-/){
($i, $ma, $a, $desc) = split(/\@/, $x);
$ctr++;
if($ctr!=$i){
&errorPage("ERROR in reading abilities.txt file (unmatched ctr=$ctr, i=$i).");
}
push @Abilities, trim($a);
push @AbilitiesMa, trim($ma);
$desc=trim($desc);
push @AbilitiesDesc, $desc;
$desc=~s/\'/\\\'/g;
$desc=~s/\"/\\\'/g;
push @AbilitiesDescJS, $desc;
$AbilityToN{$ma}=$ctr;
}else{
$n=$#all+10;
}
}
}
&setupAbilitiesParrallelArray;
###check consistency with old method###
for($i=0;$i<@AbilitiesOLD;$i++){
if($AbilitiesMa[$i] ne $AbilitiesMaOLD[$i]){
$ii=$i+4;
errorPage("abma:$ii, new:$AbilitiesMa[$i], old:$AbilitiesMaOLD[$i].");
#.qq^
#
^.join("/",@AbilitiesMa)."
".join("/",@AbilitiesMaOLD).
#"
");
}
}
########### The following @AbilitiesParrallelArray is no longer used ##########
sub setupAbilitiesParrallelArray{
@AbilitiesParrallelArray=(
"Deadly Strike", 'DeadlyStrike', #4
"Double Shield", 'DoubleShield', #5
"Double Attack", 'DoubleAttack', #6
"Counterstrike / Evil Eye / Arcane Riposte", 'CounterStrike', #7
"Wounded Smash",'WoundedAttack', #8
"Frenzy 16", 'Frenzy', #9
"Stealth Dodge", 'StealthDodge', #10
"Mind Shackle 20", 'MindShackle', #11
"Chomp 0/16 ", 'Chomp', #12
"Unleashed Fury", 'UnleashedFury', #13
"Paralyzing Stare 16",'ParalyzingGaze',#14
"Crippling Gaze 15", 'CripplingGaze', #15
"Rapid Fire", 'RapidFire', #16
"Wait Then Fire (see implemntation$star)", 'WaitAndFire', #17
"Hive Swarm", 'HiveSwarm', #18
"Zettian Targetting", 'ZettianTargetting', #19
"Shocking Grasp", 'BonusSkull', #20
"Tough", 'BonusShield', #21
"Blood Hungry", 'BloodHungry' , #22
"Stealth Armor 15", 'StealthArmor', #23
"Shield Wall (modified$star)", 'ShieldWall' #24
,"Rod of Negation 16", 'RodOfNegation' #25
,"Marro Plague 16 (simplified$star)", 'MarroPlague' #26
,"Shaolin Assault (simplified$star) / Whirlwind Attack", 'ShaolinAttack' #27
,"Whip 12", 'Whip' #28
,"Stare of Stone 7/17", 'StareOfStone' #29
,"Sharpshooter 19", 'Sharpshooter' #30
,"Ullar Rifle", 'UllarRifle' #31
,"Blood Lust", 'BloodLust' #32
,"Poison Acid Breath 8/17", 'PoisonAcidBreath' #33
,"Ice Shard Breath / Shield Throw", 'IceShardBreath' #34
,"Throw 14", 'Throw14' #35
,"Gueglix Gun 333", 'Gueglix333' #36
,"Gueglix Gun 22221", 'Gueglix22221' #37
,"Gueglix Gun 1x9", 'Gueglix1x9' #38
,"Gueglix Gun 2223", 'Gueglix2223' #39
,"Rod of Negation & no attack until negated", 'RodOfNegationVSWS' #40
,"Arrow Volley / Zombie Onslaught / Encircle", 'ArrowVolley' #41
,"Blind Rage", 'BlindRage' #42
,"Stab in the Back", 'StabInTheBack' #43
,"Maul / Venomous Sting", 'Maul' #44
,"Disappearing Ninja 12", 'DisappearingNinja' #45
,"Net Trip 14", 'NetTrip14' #46
,"One Shield Defense", 'OneShieldDefense' #47
,"Tough x2", 'Toughx2' #48
,"Smoke Powder 13", 'SmokePowder' #49
,"Valkyrie Dice Attack Bonus", 'AttackAura46' #50
,"Valkyrie Dice Defense Bonus", 'DefenseAura36' #51
,"Leaf of Home Tree Bonus", 'HomeTree' #52
,"AutoLoad", 'AutoLoad' #53
,"Gift of Empress Bonus", 'DefenseAura59' #54
,"Soul Devour / Mind Shackle 19", 'MindShackle19' #55
,"Toxic Skin 17", 'ToxicSkin' #56
,"Toxic Skin 17 vs melee squad", 'ToxicSkinVsMeleeSquad' #57
,"Helm of Mitonsul", 'HelmOfMitonsul' #58
,"Helm of Mitonsul vs melee squad", 'HelmOfMitonsulVsMeleeSquad' #59
,"Zombies Rise Again / Life Drain", 'ZombiesRiseAgain' #60
,"Web Attack", 'WebAttack' #61
,"Spidey Sense 11", 'SpideySense' #62
,"Spider Sense 14", 'SpiderSense' #63
,"Dust of Death", 'DustOfDeath' #64
,"Dust of Death vs melee squad", 'DustOfDeathVsMeleeSquad' #65
,"Vanish 9", 'Vanish9' #66
,"Rage Smash 5", 'RageSmash5' #67
,"Stomp 3 / Earth Slam (vs melee squad$star)", 'Stomp' #68
,"Eternal Hatred", 'EternalHatred' #69
,"Chilling Touch", 'ChillingTouch' #70
,"Chilling Touch vs ranged squad", 'ChillingTouchVsRangedSquad' #71
,"Trample (simplified$star)", 'Trample' #72
,"Trample vs ranged squad", 'TrampleVsRangedSquad' #73
,"Stinger Drain", 'StingerDrain' #74
,"Concentrated Will", 'ConcentratedWill' #75
,"Machine Pistol 2222", 'Gueglix2222' #76
,"Quick Release / Wrist Rocket 44", 'Gueglix44' #77
,"Dragon Heal", 'DragonHeal' #78
,"Dragon Swoop", 'DragonSwoop' #79
,"Cosmic Blast", 'CosmicBlast' #80
,"Lethal Sting", 'LethalSting' #81
,"Chalice of Fortitude", 'ChaliceOfFortitude' #82
,"Coil Crush", 'CoilCrush' #83
,"Poison Sting/Venom Ray", 'PoisonSting' #84
,"Entangling Web", 'EntanglingWeb' #85
,"Saber Storm 1x6", 'SaberStorm1x6' #86
,"Saber Storm 222", 'SaberStorm222' #87
,"Saber Storm 33", 'SaberStorm33' #88
,"Majestic Fires", 'MajesticFires' #89
,"Defense Agility (see note$star)", 'DefenseAgility' #90
,"Pounce Vs Range (see note$star)", 'PounceVsRange' #91
,"Pounce Vs Melee (see note$star)", 'PounceVsMelee' #92
,"Concealment 19 at range 2 (see note$star)", 'StealthArmor17' #93
,"Concealment 10 at range 2 (see note$star)", 'StealthArmor8' #94
,"Cell Divide 17", 'CellDivide17' #95
,"Circuitry Overload", 'CircuitryOverload' #96
,"EMP Response", 'EMPResponse' #97
,"Combined Arbalest", 'CombinedArbalest' #98
,"Ullars Bolt 16", 'UllarsBolt' #99
,"Ninjutsu Barrage", 'NinjutsuBarrage' #100
,"Feral Rage (simplified$star)", 'ShaolinAttack3' #101
,"Blood Frenzy 3", 'BloodFrenzy3' #102
,"Regenerate / Cold Healing", 'Regenerate' #103
,"Poison Weapon 12", 'PoisonWeapon' #104
,"Hide In Darkness/Shadows 16", 'StealthArmor16' #105
,"Healing From Ana Karithon (see note$star)", 'HealingFromAna' #106
,"Cleave", 'Cleave' #107
,"Fire Blast 2", 'FireBlast2' #108
,"Fire Blast 3", 'FireBlast3' #109
,"Fire Blast 4", 'FireBlast4' #110
,"Fire Blast 2 then 4 strategy (see note$star)", 'FireBlast24' #111
,"Wraith Attack using 1 drow", 'WraithAttack1drow' #112
,"Wraith Attack using 2 drow", 'WraithAttack2drow' #113
,"Wraith Attack using 3 drow", 'WraithAttack3drow' #114
,"Lurking Ambush / First Assault 3 (see implemntation$star)", 'FirstAssault3' #115
,"Air Mastery (vs flyers only)", 'AirMastery' #116
,"Searing Intensity 14 (see implemntation$star)", 'SearingIntensity' #117
,"Hydra Heads", 'HydraHeads' #118
,"Cloud Of Darkness", 'CloudOfDarkness' #119
,"Triple Attack", 'TripleAttack' #120
,"Engagement Strike 15 vs melee using ranged attack ", 'EngagementStrike15usingRange' #121
,"Engagement Strike 15 vs melee using melee attack", 'EngagementStrike15usingMelee' #122
### To be implemented: EngagementStrike15usingRange EngagementStrike15usingRange
);
@AbilitiesOLD=();
@AbilitiesMaOLD=();
%AbilityToNOLD=();
$ctr=4;
for($i=0;$i<@AbilitiesParrallelArray;$i+=2){
push @AbilitiesOLD, $AbilitiesParrallelArray[$i];
push @AbilitiesMaOLD, $AbilitiesParrallelArray[$i+1];
$AbilityToNOLD{$AbilitiesParrallelArray[$i+1]}=$ctr;$ctr++;
}
}
$maxD20Bonus=13;
%D20hash=(
0,0,
1,1,
2,2,
3,3,
4,4,
5,5,
6,6,
7,7,
8,8,
9,-1,
10,-2,
11,-3,
12,-4,
13,-5);
$maxSquadSizeIndex=7;
%squadSizeDisplay=(
0,"---",
1, "1 (common hero)",
2, "2 (squad)",
3, "3 (squad)",
4, "4 (squad)",
5, "2 (common heroes)",
6, "3 (common heroes)",
7, "4 (common heroes)"
);
%squadSize=(
0, 0,
1, 1,
2, 2,
3, 3,
4, 4,
5, 2,
6, 3,
7, 4
);
%IsSquadForPowers=(
0, "False",
1, "False",
2, "True",
3, "True",
4, "True",
5, "False",
6, "False",
7, "False"
);
@ForbiddenPairs=( #### Use forbidden.txt instead!!
(6,19), (10,21), (17,13), (6,27), (42,44), (42,53), (44,53), (11,55),(51,54), (91,92)
, (91,91),(97,97),(19,98), (107,108), (107,109),(108,109),
(13,110),(13,111),(13,112), (17,110),(17,111),(17,112)
);
if(open(FORBIDDEN, ";
close(FORBIDDEN);
chomp @all;
@ForbiddenPairs=();
foreach $x (@all){
($y, $z) = split(/:/, $x);
@y = $z =~/\d+/g;
for($i=1; $i<@y; $i++){
for($j=0; $j<$i; $j++){
push @ForbiddenPairs, ($y[$i], $y[$j]);
}
}
}
### print "(" . join(",", @ForbiddenPairs) .")\n"; <>;
}
if(open(FORBIDDEN, ";
close(FORBIDDEN);
chomp @all;
@ForbiddenPairsOpps=();
foreach $x (@all){
($y, $z) = split(/:/, $x);
@y = $z =~/\d+/g;
for($i=1; $i<@y; $i++){
for($j=0; $j<$i; $j++){
push @ForbiddenPairsOpps, ($y[$i], $y[$j]);
push @ForbiddenPairsOpps, ($y[$j], $y[$i]);
}
}
}
### print "(" . join(",", @ForbiddenPairsOpps) .")\n"; <>;
}
$hitfile="hitcounter.txt";
$HITCOUNTER = "[hit count not available]";
if(open(HIT, "<$hitfile")){
$HITCOUNTER=;
$HITCOUNTER++;
close(HIT);
if(($HITCOUNTER > 1) &&(open(HITW, ">$hitfile"))){ #because sometimes a zero is read from a busy file!
print HITW $HITCOUNTER, "\n";
close(HTW);
}
#### print "(" . $HITCOUNTER .")\n"; <>;
}
$emailLink=qq^mathguy^;
$ender=qq^
\n[To mathguy's homepage]
[Email $emailLink]^;
$instructions=qq^
^;
@zName=(); @zUC=(); @zHS=(); @zLife=();
@zMove=(); @zRange=(); @zAtt=(); @zDef=(); @zAbilities=();
@zPoints=();
@zSize=();
@zAttEnh=();
@zDefEnh=();
@zInitEnh=();
@zD20Enh=();
%zNameToIndex=();
if(open(F, ";
while( $line && ($line !~ /---begin data---/) ){$line=;}
while($line=){
if($line=~/\w/){
chomp $line;
@a = split(/\s*,\s*/, $line);
push @zName, $a[0];
push @zUC, $a[1];
push @zHS, $a[2];
push @zLife, $a[3];
push @zMove, $a[4];
push @zRange, $a[5];
push @zAtt, $a[6];
push @zDef, $a[7];
push @zAbilities, $a[8];
push @zSize, $a[9];
push @zPoints, $a[10];
if($a[11] eq ""){$a[11]=0;}push @zAttEnh, $a[11];
if($a[12] eq ""){$a[12]=0;}push @zDefEnh, $a[12];
if($a[13] eq ""){$a[13]=0;}push @zInitEnh, $a[13];
if($a[14] eq ""){$a[14]=0;}push @zD20Enh, $a[14];
$zNameToIndex{$a[0]}=$#zName;
}
}
}
&initializeJS;
print qq^ Heroscape matchup calculator$js
\n
^;
### app logic #############################################
if($formHash{"request"} eq "findprob") {
&findprob("");
}
elsif($formHash{"request"} eq "instructions") {
&instructions("");
}
elsif($formHash{"request"} eq "todo") {
&todo("");
}
elsif($formHash{"request"} eq "aboutthis") {
&aboutthis("");
}
elsif($formHash{"request"} eq "stats") {
&statspage("");
}
else {
&default_page("");
}
### end app logic #########################################
#################################################################
sub instructions {
$message = $_[0]; # an optional message
my $version4=qq^
Select type of battle.
- Simple battle: This uses the stats and abilities as entered and disregards range.
(In other words, this is the original matchup program.)
- Ranged vs melee battle:
This takes into account that the ranged units will often get free attacks on the melee units
as the melee units rush the ranged units.
The RANGE stat of the ranged unit and the MOVE stat of the melee unit are used to calculate
how many turns are required for the melee unit to reach the ranged unit.
Examples:
- RANGE 6 vs MOVE 5. Then the melee unit requires no extra turns to reach the ranged unit.
In effect, the only advantage the ranged unit has in this example is that it always gets to attack
first.
- RANGE 6 vs MOVE 4. The melee unit will require 2 moves to get to the ranged unit.
That is, a melee unit will not be able to attack on its 1st turn, but will be able to attack on its
2nd turn (unless it has the Berserk ability).
- RANGE 9 vs MOVE 4. The melee unit will require 2 moves to reach the ranged unit.
- RANGE 10 vs MOVE 4. The melee unit will require 3 moves to reach the ranged unit.
^;
$version4="";
$abils="";
for($n=0;$n<@Abilities;$n++){
$abils.=qq^
$Abilities[$n]:
$AbilitiesDesc[$n]
^;
}
print<Instructions
$message
For each side of the match up, select each of the following:
$version4
-
You can use the optional drop-down menu to select an army card. This will automatically select
all the stat numbers and abilities for that army card.
You do not have to use this;
you can customize your own stats and abilities.
But if chosen, the point box will also display point value.
- Choose Hero or Squad(s)
- If single Hero, select the number of LIFE points.
-
If Squad or common hero, select the number of figures; each figure
automatically has 1 LIFE point.
If the number of figures exceed the squad size, then select the Squad SIZE also.
Note that a squad of size 1 is interpreted as a common hero;
this allows you to model multiple common heros.
If you are doing a single figure with just 1 LIFE, then in general it does not matter
whether you select Hero with 1 LIFE or Squad with 1 UNIT; the
only time this matters is if the opponent has the Chomp, Stare of Stone, or any ability
that distinquishes between hero and squad units.
- ATTACK: select the number of attack dice. Note that at least one
of the two figures must have at least 1 ATTACK. (Currently, there are no
official figures with 0 attack. But you might want to compare say, how
Ne-Gok-Sa would do against Krug if Ne-Gok-Sa only used Mind Shackle
and no regular attack. Also you can imagine in the future where
a figure with 0 attack might be used to slow down attackers. In that case,
a relevant piece of data might be: what is the expected number of turns
that this figure can survive?)
NOTE: If a figure has a modified attack of 0, then no attack roll is made.
- DEFENSE: select the number of defense dice.
- Then select any combination of abilities.
Note that for programming reasons,
certain ability combinations are not permitted;
you will be warned upon execution if such combinations are selected.
$abils
Meaning of the returned data.
- The light blue boxes summarizes the stats and abilities.
The example above shows Krug versus Tagawa samurai with 2 experience markers.
- In the table labeled "Battle begins at the beginning of a round, with an initiative roll",
the assumption is the the initiative rolled at the beginning of a round
to determine who goes first.
In the above example, Krug wins 48.210% of the time and the Samurai win
51.790%.
- The average attacks column gives the average length of a battle
in terms of the number of attacks.
So for single figures with no multiple attacks or frenzies,
this would be the same as the number of half-turns.
In the above example, the battle would last 7.07 attacks. This is not the same
as number of turns because Krug has double attack and also because the Samurai form
a squad.
- The average survivors column gives the average number of LIFE or UNITs left
if that player wins.
If the Krug wins, on average he has 2.5 life left.
If the Samurai win, on average they have 2.0 figures left.
- In the bottom table labeled
"If we specify who goes first and battle begins which round",
the scenarios are broken down according to when
the battle begins and who has initiative.
Note that the initiative is still rerolled at the beginning of subsequent rounds.
There are 6 rows of results that correspond to each scenario
whether the melee begins at turn #1 or #2 or #3 of a round,
and whether player #1 or #2 has the initiative on the very first round.
In the above example, note that Krug does best if the battle begins in the first turn
AND he has initiative (win at 51.20%)
or if the battle begins in the 3rd turn of the round and the enemy has initiative
(win at 61.47%).
Krug has a losing percentage in the other 4 scenarios.
I will leave it up to you to reason out why Krug does best if the battle begins the
3rd turn with the enemy having the initiative!
-
The numbers such as transition matrix size and matchcode
are used for diagnostics.
Include this info in any bug report emailed to mathguy.
-
The bottom gives time spent on a calculation.
It might also say the the data was read from a database
because the calculation was already done previously.
WHOLEPAGE
################################# deleted Marro Plague note ######################
#(
#A future feature might try to do attempt the following model:
#Assume each attacker is adjacent to only one defender.
#The number of such adjacency is modeled as [previous adjacent] minus
#[killed in previous turn] plus [squad size]. This models the optimum strategy of
#moving unadjacent attackers to maximize the number of enemies adjacent.
#But this model would require keeping track of more information and is likely to take too long
#in a calculation with large squads.
#)
}
#################################################################
sub todo {
$message = $_[0]; # an optional message
print<$message
To do list
- Future abilities as they come out.
- Adding the option of giving one of the players some free attacks at the beginning to simulate
melee characters getting to the ranged characters.
This might never be implemented because it would increase the size of the calculation too much.
Future features?
-
The web interface can be improved by adding Javascript features, such as:
- Hovering over an ability will activate a bubble that describes the ability
- A selection box of current figures/squads will allow a select that will
automatically input all the data for that figure/squad.
[DONE!]
- Hitting calculate will bring up a dialog box, estimating the length of time
required for the calculation. (I'll need more data to be able to guess
the length of time based on size of transition matrix.)
- Send suggestions to $emailLink
Version history
- Version 1.0: the first working version.
But this had a typo that affected results of squads (those results were a little bit off).
- Version 2.0: Corrected typo/bug in Version 1.0 that affected squads.
This version now allows the numbers of units to be higher than the squad size, so that you can
simulate using multiple copies of a squad.
Also implements Rage.
- Version 2.1: corrects the typo that had labeled "Tough" in the wrong spot.
(I accidentally relabeled Bonus Skull to Tough.)
All Version 2.0: calculations with Tough had wrong answers - sorry!
- Version 2.2: Major improvements on efficiency when dealing with multiple common squads.
(This reduced the size of the transition matrix in these cases.)
Version 2.21 made minor improvements.
-
Version 2.3: improvements: Interface renovated to be clearer. More abilities added: Blood Hungry,.
Also, a minor typo in "Frenzy" ability fixed.
(Calculations done with previous versions involving "Frenzy" might be off by less than 0.1%.)
-
Version 2.31: Implemented Stealth Armor, Shield Wall. Fixed typo in Crippling Gaze. (Previous Crippling Gaze calculations would just fail because
the transition matrix would be detected to be nonstochastic - a double check that is always done.)
-
Version 2.32: Fixed typo in Unleashed Fury. (Previous calculations would just fail because
the transition matrix would be detected to be nonstochastic.) Hopefully, these are the last typos
introduced when Version 2.2 efficiency improvements were made.
-
Version 2.40: Interface now has a quick selection box for existing Heroscape characters; selecting one
will fill in all stats & abilities for that army card.
Warnings will sound if certain combinations are selected such as Grimnak versus Charos, in this example, you
should select "Grimnak (vs Large or Huge)" instead of "Grimnak".
And really fixed CripplingGaze this time (hopefully).
- Version 2.41: Paralyzing Stare warning added (requires opponent to be small or medium).
- Version 2.42: A textbox that shows point value if official units are chosen (prorated if fractional squads).
- Version 3.1: New interface that is more space efficient.
Created Attack Enhancement and Defense Ehancement selection boxes, for abilities
such as Sword of Reckoning and Melee Defense.
This is now needed because of the Rod of Negation! (It used to be that you could
just enter a higher Attack of Defense number.)
Abilities from Zanafor's Discovery, Raknar's Vision, and Thaelunk Tundra added.
- Version 3.12: HiveSwarm fixed.
- Version 3.2: Radio buttons added for limiting number of attackers when enemy is down to 1 figure.
This is mainly used in conjunction with HiveSwarm. Set this number to 6 if enemy is a one-hex-base figure and
to 8 if enemy is a two-hex-base figure.
- Version 3.21: More documentation.
- Version 3.30: Some abilities and figures from Thora's Vengeance added.
- Version 4.00: Abilities from Thora's Vengeance, Sir Hawthorne, Flagbearers, Dawn of Darkness added.
The Valkyrie dice abilities required revamping the way probabilities were computed using generating functions.
- Version 4.01: Fixed some typos.
(Previous version crashed on some inputs, but did not return any false results.)
- Version 4.11: Some Marvel characters added. Rest to be added later.
-
Version 4.20: WaitAndFire and UnleashedFury fixed (typos were introduced in Version 4.0).
-
Version 4.21: Fixed Throw14 when vs squads.
-
Version 4.40: Wave 7 and Swarm of the Marro and rest of Marvel characters implemented (except for Silver Surfer).
-
Version 4.50: Cosmic Blast implemented.
-
Version 4.60: Lethal Sting implemented.
-
Version 4.70: Updated the database of abilities that should not be used together.
Fixed database of previous calculations, where Throw14 versus squads, HomeTree vs CounterStrike,
Unleashed Fury with more than 1 squad were done incorrectly.
-
Version 4.80: Smoke Powder fixed so that it occurs before every attack, not just once per turn.
- Version 4.81: Added Master Win Chiu Woo.
- Version 4.82: Fixed HomeTree for heros. Any calculations involving heros using the Leaf of HomeTree aura should be recalculated.
(there were 13 of these calculations - they have been purged from the database).
- Version 5.00: Added Wave 8 abilities except for Pounce, which will be added soon, with an explanation of how it is implemented.
- Version 5.01: Entangling Web fixed. Fixed Eternal Hatred.
- Version 5.02: The ability Pounce is added.
- Version 5.03: Agent Skahen added.
- Version 5.04: Fixed stats of Marcu.
- Version 5.10: Fixed StingerDrain (StingerDrain calculations prior to 4-26-09 are incorrect.
Those calculations have been expunged from the database.).
- Version 5.20: Fixed DoubleShields (Shields of Valor). A typo bug was introduced in
Version 4.00 (May 15, 2007). D'oh. Calculations done with DoubleShields between
May 15, 2007 and May 21, 2009 are incorrect.
- Version 5.30: Added Wave 9. Also fixed a bug in Rod of Negation where the rod was not used if
it was at the very end of a round (it makes about 0.5-2.5% difference).
Also fixed Pounce, which was not killing the pouncer sometimes.
It might be worthwhile to redo any previous Rod of Negation or Pounce calculations. (Sorry.)
Any Rod of Negation or Pounce data from the data base has been expunged (88 of these).
- Version 5.40: Added Wave 10 and D&D Master Set figures.
Also fixed WaitAndFire again. Arg.
Any WaitThenFire data from the data base has been expunged (690 of these).
Remember the implementation of WaitThenFire is that the figures get +1 attack
except on the very first turn if they go first; this simulates them
being the ones to move into range of the battle.
- Version 5.45: Fixed BloodFrenzy (Sorry I misread the card to be a Krug-like ability.)
Any calculations done with the Feral troll using testing beta version before Feb 14, 2010 are incorrect.
-
Version 5.50: Added Wave 11 (D1) figures.
Implemented Engagement Strike / Ice cold.
-
Version 5.60: Implemented Squad of size 1 to mean common heroes. (For example, this makes a difference
for Sudema and Braxas.) Added negative modifiers for D20Enhancement. (Useful for custom figures.)
-
Version 6.00: New interface improvements, including mouseover activating balloons that describe the abilities.
-
Version 6.10: Added implementable abilities from D12: the fledgling abilities and Psionic Blast. Also fixed a typo that would have affected results of ChillingTouch and Trample and made them worse than they should be. All Chilling Touch and Trample and Throw numbers before Aug 28, 2010 should be redone. There were 883 such calculations from before and this data has been expunged from the database.
-
Version 6.11: Modified Searing Intensity.
-
Version 6.20: Added more choices to "Squad size" to include squads of more than 1 common heroes, which is essentially what you get when you use Wyrmling Bonding or Master of the Elements. This only makes a difference for powers that distinguish squad figures from common heros such as Chomp, Poison Acid Breath, Stare of Stone.