So, those of you who've been following my profile status updates closely may have picked up on the fact that I've made myself a new toy to play with. Having been wanting to give Age of Sigmar a go for a while (on the sole condition that I can still call my lizzies Lizardmen) and also wanting to know how well my army will stack up against the other races out there, I decided to put my expensive university education to use and write a little text-based application to test things. Ladies and Gentleslann, I present... The Age of Sigmar Fight-O-Matic 0.6! Download/Clone/Fork it on GitHub! Or just download the .exe and data folder! Okay, let's get the boring stuff out the way first: this is very much in the early stages, the Fight-O-Matic can functionally simulate melee combat (ranged combat to come very soon) between two units to its end (one side entirely eliminated), and run a user-defined number of repetitions in order to crunch out some averages and other interesting numbers (average number of units left after a win, etc.). There are currently no special abilities or upgrades (including weapons with weird rules), though I will try and include these at a later date. It also makes some big simplifications with regards to things like unit positioning/front size, as adding things like model positions and actual range checks would also mean having to program things like movement, pile-ins, etc. Basically it would be a nightmare, so I've kind of abstract-ified it so only a maximum number of models from each side can fight at a time. I will stress that as a result of the above, the Fight-O-Matic is really only intended as a rough guide - something that gives you a general idea of 'how does unit x stack up against unit y'? I will accept no liability in the event of chronic embarrassment and/or upstaging at tournaments, nor the wetting of the floor at your local Gee-Dubs as a result of the Stormcast Goldfags having a super special wank ability that drowns your models in Holy Sigmajizz(tm), that the Fight-O-Matic didn't include in its calculations. Finally (finally) I'll add that the program is, at the moment, a bit of a mess and the output it produces may not be sparklingly formatted, and it'll crash if you do some bad input. I do very much intend to tidy things up soon though. 'Alright how do I use this thing?' To run the simulator, run the .exe, making absolutely sure you've extracted the files from the .zip and placed the 'data' folder and the .exe in the same directory. Simply enter the name of a unit (no prefixes/faction names required), the number of models you want, and repeat for the second side. Enter the number of battles you'd like to run and whack enter. The FOM will do its thing and spit out some tidy statistics at the end. Neat, right? As-is, the FOM reads faction/model/weapon data from a series of Lua files (stored in the 'factiondata' directory), adding new models, weapons, or entirely new factions can be done yourself if you're familiar with it, and even if you aren't I've tried to make the layout as intuitive as possible. As I sadly don't have the time to add every single model myself, right now there are only a very few models from the Seraphon faction. I've linked the GitHub repository so we can turn this into a collaborative effort if anybody fancies adding their own data. In the meantime, if you want two units to be tested against one another but don't know how to do XML Lua or any of that wank, then please leave a reply to this thread with your request and I'll sort it all out for you and post the results! Here are some features I'm planning to add soon, in rough order of priority. 1. Ranged combat. 2. Selector for batch/individual battles. (DONE) 3. Unit upgrades. 4. Special weapons and abilities. 5. Build a GUI-based version off of the current logic. Do have fun, and if you have any suggestions or bug reports (besides the obvious) or whatever then please let me know. Again, any contributions of model/weapon XML files would really be appreciated, both by myself, and anybody else who wants to use this thing.
Hmmmm..... interesting. Although I fear that some abilities (useful ones to boot) will be VERY hard to simulate halfway accurately, especially those that are either dependent on movement or cause it. btw: which version of Visual Studio does it work with best? Any of the free ones?
Oh man, doing any of the ones like 'wary fighters' or those things would kind of require me to add actual unit positions and the ability for the user to set them. In fact it would be easier for me just to write a full-fledged graphical AoS tabletop simulator-y thing. I'm keeping the scope of this to straight-up combat, so I'll stick to implementing any upgrades/abilities that directly affect melee combat - re-rolls, rend ignores, etc, since those are still pretty important I think. I used Visual Studio Community 2017, haven't tried it with any other versions.
@GreenyRepublic interesting project! Looks great so far. Its a good idea to keep it MVP (minimal viable product) and flesh it out with more features like you plan, this allow for feedback from users, and a faster development cycle between releases.
@GreenyRepublic if you try adding calculations having to do with "unit positions" maybe limit the scope of this to a specific and admittedly rare positional situation: A Bridge Fight Assume that both units are facing each other on a bridge that is table length long and 1 to 10 inches wide. Maybe the user could set the width (?) That constraint ought to simplify a few things...the numbers that can pile in and such.
Hi there, great thing to start programming such a combat simulator. I added some units (ironjawz), run some fights, and thought it would be very useful to implement "ignore rend" in the program. I saw your list of "things yet to do" and think this would be a major point, as actually the outcome of the calculated fights are quiet ... disappointing, as long as enemy's rend is not negated. All in all a cool thing, glad you made it!
Yeah, there are some fairly important things that aren't yet in place, however they are definitely on the way. I just can't say exactly when I'll have them done. Would you be interested in contributing your Ironjawz XML files to the project? If you don't use GitHub then feel free to just attach them here in a .zip or something.
I don't use/have github. I could mail them to you, just pm me. As I'm on a business trip earliest by Saturday this week. Another thing I came across : In the data files you already implement unit sizes. When running the program you still have to enter the "Number of Models". A) is this number n for multiples of the chosen unit's size (n=2, so 2*10 saurus warriors), or B) is n the number of models in the unit (n=6, 6 saures warriors)? Proposal: hitting return tells the program to use the standard value of the unit size as in the data file, whereas entering a number tells the program to use n models to calculate combat.
Big-ass update! I rewrote the entire app in C++, made a fancy menu, and did some other shizzle. Still a heavy WIP but give it a look! Batch battle number crunches will now be outputted to a .txt file in a folder in whichever directory you put the program. Enjoy! EDIT: There's no direct .exe download link for this one, just the GitHub page link. Download it as a .zip and run the .exe if you want to use it.
BIG UPDATE We're in the Lua world now, so all of this: Spoiler Code: <faction name = "Seraphon"> <model name = "Saurus Warriors (Spears)"> <stats> <move>5</move> <save>5</save> <bravery>10</bravery> <wounds>1</wounds> </stats> <size>10</size> <cost>100</cost> <weapons> <weapon type ="melee">Celestite Spear</weapon> <weapon type ="melee">Powerful Jaws and Stardrake Shield</weapon> </weapons> <upgrades></upgrades> <abilities></abilities> </model> <model name = "Saurus Warriors (Clubs)"> <stats> <move>5</move> <save>5</save> <bravery>10</bravery> <wounds>1</wounds> </stats> <size>10</size> <cost>100</cost> <weapons> <weapon type ="melee">Celestite Club</weapon> <weapon type ="melee">Powerful Jaws and Stardrake Shield</weapon> </weapons> <upgrades></upgrades> <abilities></abilities> </model> <model name = "Saurus Guard"> <stats> <move>5</move> <save>4</save> <bravery>10</bravery> <wounds>1</wounds> </stats> <size>5</size> <cost>100</cost> <weapons> <weapon type ="melee">Celestite Polearm</weapon> <weapon type ="melee">Powerful Jaws and Stardrake Shield</weapon> </weapons> <upgrades></upgrades> <abilities></abilities> </model> <model name = "Skinks (Javelins)"> <stats> <move>8</move> <save>6</save> <bravery>10</bravery> <wounds>1</wounds> </stats> <size>10</size> <cost>80</cost> <weapons> <weapon type ="ranged">Meteoric Javelin</weapon> <weapon type ="ranged">Boltspitter</weapon> <weapon type ="melee">Meteoric Javelin (M)</weapon> <!--weapon type ="melee">Boltspitter (M)</weapon--> <!--weapon type ="melee">Moonstone Club</weapon--> </weapons> <upgrades></upgrades> <abilities></abilities> </model> <model name = "Skinks (Boltspitters)"> <stats> <move>8</move> <save>6</save> <bravery>10</bravery> <wounds>1</wounds> </stats> <size>10</size> <cost>80</cost> <weapons> <weapon type ="ranged">Boltspitter</weapon> <weapon type ="melee">Boltspitter (M)</weapon> <!--weapon type ="melee">Moonstone Club</weapon--> </weapons> <upgrades></upgrades> <abilities></abilities> </model> <model name = "Kroxigor"> <stats> <move>8</move> <save>4</save> <bravery>10</bravery> <wounds>4</wounds> </stats> <size>3</size> <cost>180</cost> <weapons> <weapon type ="melee">Drakebite Maul</weapon> <weapon type ="melee">Vice-Like Jaws</weapon> </weapons> <upgrades></upgrades> <abilities></abilities> </model> </faction> Now looks a bit like this: Spoiler Code: --Abilities declared globally as most are used multiple times -- These are all incomplete and unimplemented as the mechanisms need to be added to the cpp side StardrakeIcon = {} Wardrum = {} StardrakeShield = { name = "Stardrake Shield", type = "", effect = function () end } StarBuckler = StardrakeShield OrderedCohort = { name = "OrderedCohort", type = "", effect = function () end } EnergyTransference = {} SwornGuardians = { name = "Sworn Guardians", type = "", effect = function () end } faction = { name = "Seraphon", models = { --[[{ name = "Sample Model", stats = { move = 4, save = 4, bravery = 8, wounds = 2 }, matchedData = { unitSize = 5, unitCost = 100 }, keywords = { "ORDER", "SOMETHING", "LOOKAKEYWORD" }, meleeWeapons = { { name = "Sword", range = 1, attacks = 1, toHit = 3, toWound = 4, rend = -1, damage = 2 } },--]] { name = "Saurus Warriors (Clubs)", stats = { move = 5, save = 5, bravery = 10, wounds = 1 }, matchedData = { unitSize = 10, unitCost = 90 }, keywords = { "ORDER", "DAEMON", "CELESTIAL", "SERAPHON", "SAURUS", "SAURUS WARRIORS" }, meleeWeapons = { { name = "Celestite Club", range = 1, attacks = 1, toHit = 4, toWound = 3, rend = 0, damage = 1 } , { name = "Powerful Jaws and Stardrake Shield", range = 1, attacks = 1, toHit = 5, toWound = 4, rend = 0, damage = 1 } }, abilities = { StardrakeIcon, Wardrum, StardrakeShield, OrderedCohort } }, { name = "Saurus Warriors (Spears)", stats = { move = 5, save = 5, bravery = 10, wounds = 1 }, matchedData = { unitSize = 10, unitCost = 90 }, keywords = { "ORDER", "DAEMON", "CELESTIAL", "SERAPHON", "SAURUS", "SAURUS WARRIORS" }, meleeWeapons = { { name = "Celestite Spear", range = 2, attacks = 1, toHit = 4, toWound = 4, rend = 0, damage = 1 } , { name = "Powerful Jaws and Stardrake Shield", range = 1, attacks = 1, toHit = 5, toWound = 4, rend = 0, damage = 1 } }, abilities = { StardrakeIcon, Wardrum, StardrakeShield, OrderedCohort } }, { name = "Saurus Guard", stats = { move = 5, save = 4, bravery = 10, wounds = 1 }, matchedData = { unitSize = 5, unitCost = 100 }, keywords = { "ORDER", "DAEMON", "CELESTIAL", "SERAPHON", "SAURUS", "SAURUS GUARD" }, meleeWeapons = { { name = "Celestite Polearm", range = 1, attacks = 2, toHit = 3, toWound = 3, rend = -1, damage = 1 } , { name = "Powerful Jaws and Stardrake Shield", range = 1, attacks = 1, toHit = 5, toWound = 4, rend = 0, damage = 1 } }, abilities = { StardrakeIcon, Wardrum, StardrakeShield, SwornGuardians } }, { name = "Skinks (Javelins and Shields)", stats = { move = 8, save = 6, bravery = 10, wounds = 1 }, matchedData = { unitSize = 10, unitCost = 80 }, keywords = { "ORDER", "DAEMON", "CELESTIAL", "SERAPHON", "SKINKS" }, meleeWeapons = { { name = "Meteoric Javelin", range = 1, attacks = 1, toHit = 6, toWound = 5, rend = -1, damage = 1 } }, rangedWeapons = { { name = "Meteoric Javelin", range = 8, attacks = 1, toHit = 5, toWound = 4, rend = 0, damage = 1 } }, abilities = { CelestialCohort, StarBuckler } }, { name = "Kroxigor", stats = { move = 8, save = 4, bravery = 10, wounds = 4 }, matchedData = { unitSize = 3, unitCost = 180 }, keywords = { "ORDER", "DAEMON", "CELESTIAL", "SERAPHON", "KROXIGOR", }, meleeWeapons = { { name = "Drakebite Maul", range = 2, attacks = 4, toHit = 4, toWound = 3, rend = 0, damage = 2 } , { name = "Vice-like Jaws", range = 1, attacks = 1, toHit = 4, toWound = 3, rend = -1, damage = 1 } }, abilities = { EnergyTransference } }, } } Unfortunately code formatting on XenForo is a bit rudimentary so that all looks messier than it would do in a proper editor. Hopefully you'll all find the Lua format less cluttered and a bit more intuitive, and whilst I can't commit the time to converting all of the existing faction data, I hope to provide a comprehensive Seraphon document over time as a reference, and I will of course be here to answer any and all questions you may have about how things need to be laid out. However there's more to the motivation for this than just having more readable data - implementing the facility to handle any one of Age of Sigmar's wacky-ass abilities was going to be really, REALLY difficult in an XML format for reasons that should be obvious, and having a domain-specific-language like Lua allows us to write our own ability definitions in a reasonably intuitive manner, for example our Stardrake Shield ability may look something like this: Code: StardrakeShield = { ability = function() if getIncomingAttack().rend =< -1 getIncomingAttack().rend = 0 end end } Now here's the catch: none of this works yet, and so far all I have is model/weapon data being correctly parsed from Lua files (provided the data matches the naming conventions I've set), the C++ code is not in place to handle abilities, in fact to get this working I'll need to be doing a tonne more design groundwork than I had originally anticipated (talk about opening a can of worms). As I go down that rabbit hole I'll make a few posts here about it sharing my thought process, hopefully you'll all find that interesting! In the meantime please feel free to ask about the new format and for help with getting data files together, I appreciate any and all contributions! - GreenyRepublic