Even rolls?

This exciting new release is a faithful adaptation of the renowned Conflict of Heroes board game that won the Origins Historical Game of the Year, Charles Roberts Wargame of the Year and the James F. Dunnigan Design Elegance Award, as well as many others!

Designed and developed in cooperation with Uwe Eickert, the original designer of Conflict of Heroes, and Western Civlization Software, the award-winning computer wargame studio, no effort has been spared to bring the outstanding Conflict of Heroes gameplay to the computer. Conflict of Heroes includes an AI opponent as well as full multiplayer support with an integrated forum and game lobby. To remain true to the core gameplay of the board game, the PC version is designed to be fun, fast and easy to play, though hard to master. The game design is also historically accurate and teaches and rewards platoon and company-level combined arms tactics without overwhelming the player with rules.

Moderator: MOD_WestCiv

Post Reply
Ocean1
Posts: 61
Joined: Wed Jul 29, 2009 1:24 am

Even rolls?

Post by Ocean1 »

A question, which I hope you'd feel ok answering if it is the case: Are the rolls and chances for kill or hit rolls the same for the AI as for the human player? The first couple of games it seemed ok, but now it just seems the AI hits me a heck of a lot more than I hit it. I am playing on the default difficulty level. Maybe it's just that I am being reckless or using bad tactics, but I thought I'd ask. I know that in some past computer war games I've played, the AI was given a slight advantage - to make it "smarter" and more of a challenge.. Either way.. it's a good game.
Joram
Posts: 3206
Joined: Fri Jul 15, 2005 5:40 am

RE: Even rolls?

Post by Joram »

I'm pretty certain there are no advantages given to the AI dice. You can also always use the dice cam feature in SP.
User avatar
Erik Rutins
Posts: 39666
Joined: Tue Mar 28, 2000 4:00 pm
Location: Vermont, USA
Contact:

RE: Even rolls?

Post by Erik Rutins »

No, the AI always uses the same dice as you.

However, as you increase in difficulty, for each level above Easy it gets 25% more CAPs and an increased chance of higher unit quality. The combination of those two can lead to better combat results, but you should see when it uses its CAPs to boost combat rolls. Also, above Normal it gets 1 extra AP per unit.

Regards,

- Erik
Erik Rutins
CEO, Matrix Games LLC


Image

For official support, please use our Help Desk: http://www.matrixgames.com/helpdesk/

Freedom is not Free.
User avatar
ericbabe
Posts: 11848
Joined: Wed Mar 23, 2005 3:57 am
Contact:

RE: Even rolls?

Post by ericbabe »

Here is the code for a unit attacking another unit, and the code for generating the results of an attack:

private void AttackPiece(GameTime gameTime, Context context, int apCost, Piece targetPiece, Random rand, int attackNo)
{
CohPiece attacker = ThisPiece;
CohPiece defender = targetPiece as CohPiece;
Player attackerPlayer = context.PlayerManager[attacker.PlayerName];
Player defenderPlayer = context.PlayerManager[defender.PlayerName];

if (attacker == null || defender == null)
return;

// attack strength
String modfDesc = "";
bool frontal;
int att;
int def;
GetAttackStats(context, targetPiece, apCost, out frontal, out att, out def, ref modfDesc, false);

// reveal attacker
attacker.Hider.Unhide();

// AI player can modify attack roll with caps now
// omitted...

// get attack roll
int roll1, roll2;
int baseRoll = MakeAttackRoll(context.GameScene.HexGame, rand, context.PlayerManager[attacker.PlayerName], out roll1, out roll2);
int modfRoll = baseRoll + att;

// zero roll bonus
context.NextRollBonus = 0;

// notify for attack
String msg = "";
if (modfRoll >= def)
{
// unhide the defender
defender.Hider.Unhide();

msg = String.Format(modfRoll != baseRoll ?
Resources.CohAttacker_AttackPiece__0__rolls__4__modified_by__2__and_hits__1__whose_defense_is__3__ :
Resources.CohAttacker_AttackPiece__0__rolls__4__and_hits__1__whose_defense_is__3__,
attacker.GetName(), defender.GetName(), modfRoll - baseRoll, def, baseRoll);
}
else
{
if (defender.Hider.Hidden)
{
if (attackNo == 0)
msg = String.Format(/*ss.Get*/(Resources.CohAttacker_AttackLocation__0__does_not_reveal_any_units___1__), attacker.GetName(), baseRoll);
}
else
{
msg = String.Format(modfRoll != baseRoll ?
Resources.CohAttacker_AttackPiece__0__rolls__4__modified_by__2__and_misses__1__whose_defense_is__3__ :
Resources.CohAttacker_AttackPiece__0__rolls__4__and_misses__1__whose_defense_is__3__,
attacker.GetName(), defender.GetName(), modfRoll - baseRoll, def, baseRoll);
}

}

// add modifiers to message
if (!defender.Hider.Hidden)
{
msg += @" (";
msg += modfDesc;
msg += @")";
}

CombatSubject.Report(msg, modfRoll >= def ? ReportColors.HitColor : ReportColors.Standard);

if (!defender.Hider.Hidden)
{
// is piece hidden damage revealed?
if (defender.GetDamage() != null &&
defender.HiddenDamage &&
(defender.GetDamage().FlankDefense != 0 ||
defender.GetDamage().FlankDefenseIsArmored != 0 ||
defender.GetDamage().FrontDefense != 0 ||
defender.GetDamage().FlankDefenseIsArmored != 0))
{
defender.HiddenDamage = false;
CombatSubject.Report(String.Format(/*ss.Get*/(Resources.CohAttacker_AttackPiece__0__damage_is_revealed___1_), defender.GetName(), defender.GetDamage().Name));

}
if (attacker.GetDamage() != null &&
attacker.HiddenDamage &&
(attacker.GetDamage().Attack != 0 ||
attacker.GetDamage().AttackVsArmor != 0 ||
attacker.GetDamage().Range != 0 ||
attacker.GetDamage().MinRange != 0))
{
attacker.HiddenDamage = false;
CombatSubject.Report(String.Format(/*ss.Get*/(Resources.CohAttacker_AttackPiece__0__damage_is_revealed___1_), attacker.GetName(), attacker.GetDamage().Name));
}
}

// roll on flying text
FlyingTextManager.AddFloatFade(gameTime, String.Format(Resources.CohAttacker_AttackPiece_Roll___0_, baseRoll),
Color.White, attacker, new Vector2(0, -30.0f * attackNo), roll1, roll2);

// get roll result
if (modfRoll >= def)
{
// hit
// give damage marker
bool instantKill = (modfRoll >= def + 4);
CohDamage damage;
instantKill |= GiveDamage(defender, out damage);

// is defender dead
bool defenderIsDead = instantKill || defender.Hp() <= 0;

// hey controller
context.GameScene.Controller.AddAttackSequence(attacker, defender, defender.MapPos,
defenderIsDead ? CohController.HitResult.Destroy : CohController.HitResult.Hit,
new Vector2(0, -30.0f * attackNo),
damage.Name);

// reveal hit piece
defender.Hider.Unhide();

// destroy if necessary
if (defenderIsDead)
{
PerformCombatKill(context, attacker, defender);
}
else
{
HeadlineSubject.Report(String.Format(/*ss.Get*/(Resources.CohAttacker_AttackPiece__0__is_damaged), defender.GetName()));
}
}
else
{
if (!defender.Hider.Hidden)
{
// miss
context.GameScene.Controller.AddAttackSequence(attacker, defender, defender.MapPos, CohController.HitResult.Miss,
new Vector2(0, -30.0f * attackNo), null);
HeadlineSubject.Report(String.Format(/*ss.Get*/(Resources.CohAttacker_AttackPiece__0__misses), attacker.GetName()));
}
}

// call event
if (PieceAttackingHandler != null)
PieceAttackingHandler(gameTime, attacker, defender, defender.MapPos);
}

and the code for generating the attack roll:

private static int MakeAttackRoll(Application.HexGame game, Random rand, Player player, out int roll1, out int roll2)
{
if (PreferenceManager.Instance.AreUsingDiceCamera && player.IsHuman && !NetworkFacade.Game.IsActiveGame)
{
try
{
game.SuspendUpdate = true;
_DiceForm = new DiceForm
{
TopMost = true
};
_DiceForm.ShowDialog();
roll1 = 0;
roll2 = 0;
game.SuspendUpdate = false;
return _DiceForm.RollResult;
}
catch (Exception)
{
CombatSubject.Report(Resources.CohAttacker_MakeAttackRoll_Dice_Camera_failed_to_initialize_);
game.SuspendUpdate = false;
return Random2D6(rand, out roll1, out roll2);
}
}
else
return Random2D6(rand, out roll1, out roll2);
}

Here is the code to generate Random2D6:

private static int Random2D6(Random rand, out int roll1, out int roll2)
{
roll1 = rand.Next(6) + 1;
roll2 = rand.Next(6) + 1;
return roll1 + roll2;
}

Image
User avatar
ericbabe
Posts: 11848
Joined: Wed Mar 23, 2005 3:57 am
Contact:

RE: Even rolls?

Post by ericbabe »

Here is the code for our pseudorandom number generator. Note that it derives from System.Random and it is an instance of this class we pass as a parameter to Random2D6:

namespace HexGame.WCS.RandomNumbers
{
public class MersenneTwister : System.Random
{
// number generated since last reseed
private int NumberGenerated;

/* Period parameters */
private const int N = 624;
private const int M = 397;
private const uint MATRIX_A = 0x9908b0df; /* constant vector a */
private const uint UPPER_MASK = 0x80000000; /* most significant w-r bits */
private const uint LOWER_MASK = 0x7fffffff; /* least significant r bits */

/* Tempering parameters */
private const uint TEMPERING_MASK_B = 0x9d2c5680;
private const uint TEMPERING_MASK_C = 0xefc60000;

private static uint TEMPERING_SHIFT_U(uint y) { return (y >> 11); }
private static uint TEMPERING_SHIFT_S(uint y) { return (y << 7); }
private static uint TEMPERING_SHIFT_T(uint y) { return (y << 15); }
private static uint TEMPERING_SHIFT_L(uint y) { return (y >> 18); }

private uint[] mt = new uint[N]; /* the array for the state vector */

private short mti;

private static uint[] mag01 = { 0x0, MATRIX_A };

/* initializing the array with a NONZERO seed */
public MersenneTwister(uint seed)
{
/* setting initial seeds to mt[N] using */
/* the generator Line 25 of Table 1 in */
/* [KNUTH 1981, The Art of Computer Programming */
/* Vol. 2 (2nd Ed.), pp102] */
mt[0] = seed & 0xffffffffU;
for (mti = 1; mti < N; ++mti)
{
mt[mti] = (69069 * mt[mti - 1]) & 0xffffffffU;
}

// reset number generated
NumberGenerated = 0;
}
public MersenneTwister()
: this(4357) /* a default initial seed is used */
{
}

public String GetCurrentState()
{
return string.Format(@"{0}.{1}", mt[0].ToString(), NumberGenerated);
}

protected uint GenerateUInt()
{
uint y;

/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) /* generate N words at one time */
{
short kk = 0;

for (; kk < N - M; ++kk)
{
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1];
}

for (; kk < N - 1; ++kk)
{
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1];
}

y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1];

mti = 0;
}

y = mt[mti++];
y ^= TEMPERING_SHIFT_U(y);
y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
y ^= TEMPERING_SHIFT_L(y);

// remember we generated another one
NumberGenerated++;

return y;
}

public virtual uint NextUInt()
{
return this.GenerateUInt();
}

public virtual uint NextUInt(uint maxValue)
{
return (uint)(this.GenerateUInt() / ((double)uint.MaxValue / maxValue));
}

public virtual uint NextUInt(uint minValue, uint maxValue) /* throws ArgumentOutOfRangeException */
{
if (minValue >= maxValue)
{
throw new ArgumentOutOfRangeException();
}

return (uint)(this.GenerateUInt() / ((double)uint.MaxValue / (maxValue - minValue)) + minValue);
}

public override int Next()
{
return this.Next(int.MaxValue);
}

public override int Next(int maxValue) /* throws ArgumentOutOfRangeException */
{
if (maxValue <= 1)
{
if (maxValue < 0)
{
throw new ArgumentOutOfRangeException();
}

return 0;
}

return (int)(this.NextDouble() * maxValue);
}

public override int Next(int minValue, int maxValue)
{
if (maxValue < minValue)
{
throw new ArgumentOutOfRangeException();
}
else if (maxValue == minValue)
{
return minValue;
}
else
{
return this.Next(maxValue - minValue) + minValue;
}
}

public override void NextBytes(byte[] buffer) /* throws ArgumentNullException*/
{
int bufLen = buffer.Length;

if (buffer == null)
{
throw new ArgumentNullException();
}

for (int idx = 0; idx < bufLen; ++idx)
{
buffer[idx] = (byte)this.Next(256);
}
}

public override double NextDouble()
{
return (double)this.GenerateUInt() / ((ulong)uint.MaxValue + 1);
}
}
}
Image
Post Reply

Return to “Conflict of Heroes Series”