This commit is contained in:
Michal Vrba
2026-09-22 13:24:31 +02:00
parent d1ce9fb0b8
commit c999743438
136 changed files with 679 additions and 167 deletions
@@ -1,5 +1,6 @@
@inject IGameDataLoader DataLoader
@inject IBattleEngine BattleEngine
@implements IDisposable
<div class="battle-arena-wrapper" tabindex="0" @onkeydown="HandleKeyDown" @ref="arenaElement">
<!-- Top HUD / Level Info -->
@@ -88,7 +89,7 @@
{
<div class="equation-box @(State.IsDefensePhase ? "defense-glow" : "attack-glow")">
<span class="equation-title">
@(State.IsDefensePhase ? "🛡️ OBRANNÝ TEST: Vyber správnu odpoveď!" : "⚔️ VYPOČÍTAJ A ZAÚTOČ:")
@(State.IsDefensePhase ? "🛡️ ZÁCHRANNÁ OBRANA: Vyrieš náročnú výzvu a aktivuj štít!" : "⚔️ VYPOČÍTAJ A ZAÚTOČ:")
</span>
<h1 class="equation-text">@State.CurrentProblem.QuestionText</h1>
</div>
@@ -213,11 +214,30 @@
private double RemainingTimeSeconds { get; set; } = 6.0;
private Level? NextLevel => DataLoader.GetNextLevel(PlayerGrade, CurrentLevel.Id);
private string? _loadedLevelId;
protected override void OnInitialized()
{
if (CurrentLevel != null && !string.IsNullOrEmpty(CurrentLevel.Id))
{
_loadedLevelId = CurrentLevel.Id;
StartBattle();
}
}
protected override void OnParametersSet()
{
if (CurrentLevel != null && !string.IsNullOrEmpty(CurrentLevel.Id) && CurrentLevel.Id != _loadedLevelId)
{
_loadedLevelId = CurrentLevel.Id;
StartBattle();
}
}
public void Dispose()
{
_timer?.Dispose();
}
private void StartBattle()
{
@@ -407,6 +427,16 @@
return;
}
if (e.Key is "<" or "=" or ">")
{
var match = State.CurrentProblem.Choices.FirstOrDefault(c => c == e.Key);
if (match != null)
{
await HandleChoiceSelected(match);
return;
}
}
if (e.Key.Equals("a", StringComparison.OrdinalIgnoreCase) || e.Key.Equals("y", StringComparison.OrdinalIgnoreCase))
{
var match = State.CurrentProblem.Choices.FirstOrDefault(c => c.StartsWith("Á", StringComparison.OrdinalIgnoreCase) || c.StartsWith("A", StringComparison.OrdinalIgnoreCase));
+6 -2
View File
@@ -39,7 +39,8 @@
var currentWorld = worlds.FirstOrDefault(w => w.Levels.Any(l => l.Id == SelectedLevel.Id));
var bg = currentWorld?.Background ?? "assets/images/backgrounds/world1_meadow.jpg";
<BattleArena Hero="hero"
<BattleArena @key="SelectedLevel.Id"
Hero="hero"
CurrentLevel="SelectedLevel"
PlayerGrade="Profile.SelectedGrade"
BackgroundImage="@bg"
@@ -154,9 +155,12 @@
private void UnlockNextLevel(Level completedLevel)
{
Profile.CompletedLevels.Add(completedLevel.Id);
bool isFirstTime = Profile.CompletedLevels.Add(completedLevel.Id);
if (isFirstTime)
{
Profile.TotalStars += completedLevel.IsBoss ? 3 : 1;
Profile.Gold += completedLevel.IsBoss ? 50 : 20;
}
var worlds = DataLoader.GetWorldsForGrade(Profile.SelectedGrade);
var currentWorld = worlds.FirstOrDefault(w => w.Levels.Any(l => l.Id == completedLevel.Id));
+2 -4
View File
@@ -51,7 +51,7 @@ public class BattleEngine : IBattleEngine
public void NextProblem()
{
State.CurrentProblem = _mathGenerator.GenerateProblem(State.CurrentLevel.MathConfig);
State.CurrentProblem = _mathGenerator.GenerateProblem(State.CurrentLevel.MathConfig, State.EnemyHpPhase);
State.ProblemStartTime = DateTime.UtcNow;
State.IsDefensePhase = false;
State.PlayerPose = CombatPose.Idle;
@@ -117,9 +117,7 @@ public class BattleEngine : IBattleEngine
State.EnemyPose = CombatPose.Attack;
State.IsDefensePhase = true;
State.LastResult = CombatResultType.PlayerMissed;
State.LastFeedbackMessage = $"❌ Vedľa! Pozor na protiútok! Aktivuj štít!";
State.CurrentProblem = _mathGenerator.GenerateDefenseTest();
State.CurrentProblem = _mathGenerator.GenerateDefenseTest(State.CurrentLevel.MathConfig);
State.ProblemStartTime = DateTime.UtcNow;
return CombatResultType.PlayerMissed;
@@ -4,41 +4,99 @@ namespace CiselnaVyprava.Services;
public interface IMathProblemGenerator
{
MathProblem GenerateProblem(MathConfig config);
MathProblem GenerateDefenseTest();
MathProblem GenerateProblem(MathConfig config, int hpPhase = 1);
MathProblem GenerateDefenseTest(MathConfig config);
}
public class MathProblemGenerator : IMathProblemGenerator
{
private readonly Random _rand = new();
public MathProblem GenerateProblem(MathConfig config)
public MathProblem GenerateProblem(MathConfig config, int hpPhase = 1)
{
var op = config.Operations[_rand.Next(config.Operations.Count)];
return op switch
{
"+" => GenerateAddition(config),
"-" => GenerateSubtraction(config),
"*" => GenerateMultiplication(config),
"/" => GenerateDivision(config),
"<" or ">" or "=" => GenerateComparison(config),
_ => GenerateAddition(config)
"+" => GenerateAddition(config, hpPhase),
"-" => GenerateSubtraction(config, hpPhase),
"*" => GenerateMultiplication(config, hpPhase),
"/" => GenerateDivision(config, hpPhase),
"<" or ">" or "=" => GenerateComparison(config, hpPhase),
_ => GenerateAddition(config, hpPhase)
};
}
private MathProblem GenerateAddition(MathConfig config)
private MathProblem GenerateAddition(MathConfig config, int hpPhase)
{
// Phase 4: Possibility of 3-term chain or climax numbers
if (hpPhase >= 4 && _rand.Next(3) == 0 && config.MaxSum >= 10)
{
int c1 = Math.Max(1, config.MinA);
int c2 = Math.Max(1, config.MinB);
int maxPart = Math.Max(2, config.MaxSum / 3);
int a3 = _rand.Next(c1, maxPart + 1);
int b3 = _rand.Next(c2, Math.Min(config.MaxB, config.MaxSum - a3 - 1) + 1);
int c3 = _rand.Next(1, Math.Min(config.MaxB, config.MaxSum - a3 - b3) + 1);
int sum3 = a3 + b3 + c3;
var chainProblem = new MathProblem
{
DisplayA = $"{a3} + {b3} + {c3}",
Operation = "+",
DisplayB = "",
CorrectAnswer = sum3.ToString(),
QuestionText = $"{a3} + {b3} + {c3} = ?",
InputType = config.InputType
};
if (config.InputType == "MultipleChoice")
{
chainProblem.Choices = GenerateNumericChoices(sum3, 1, config.MaxSum);
}
return chainProblem;
}
int a, b;
int minA = config.MinA;
int maxA = config.MaxA;
int minB = config.MinB;
int maxB = config.MaxB;
// Scale difficulty by phase
if (hpPhase == 1)
{
maxA = Math.Max(minA, minA + (maxA - minA) / 2);
maxB = Math.Max(minB, minB + (maxB - minB) / 2);
}
else if (hpPhase >= 3)
{
minA = Math.Min(maxA, minA + (maxA - minA) / 3);
minB = Math.Min(maxB, minB + (maxB - minB) / 3);
}
if (config.AllowCarryOver)
{
a = _rand.Next(config.MinA, config.MaxA + 1);
b = _rand.Next(config.MinB, Math.Min(config.MaxB, config.MaxSum - a) + 1);
a = _rand.Next(minA, maxA + 1);
int remainingMax = Math.Min(maxB, config.MaxSum - a);
b = _rand.Next(minB, Math.Max(minB, remainingMax) + 1);
// In Phase 3-4, prefer carry over (sum of units >= 10) if possible
if (hpPhase >= 3 && config.MaxSum >= 20)
{
int tries = 0;
while ((a % 10 + b % 10 < 10) && tries < 15)
{
tries++;
a = _rand.Next(minA, maxA + 1);
remainingMax = Math.Min(maxB, config.MaxSum - a);
b = _rand.Next(minB, Math.Max(minB, remainingMax) + 1);
}
}
}
else
{
a = _rand.Next(config.MinA, config.MaxA + 1);
b = _rand.Next(config.MinB, config.MaxB + 1);
a = _rand.Next(minA, maxA + 1);
b = _rand.Next(minB, maxB + 1);
}
int sum = a + b;
@@ -60,10 +118,63 @@ public class MathProblemGenerator : IMathProblemGenerator
return problem;
}
private MathProblem GenerateSubtraction(MathConfig config)
private MathProblem GenerateSubtraction(MathConfig config, int hpPhase)
{
int b = _rand.Next(config.MinB, config.MaxB + 1);
int a = _rand.Next(Math.Max(b, config.MinA), config.MaxA + 1);
// Phase 4: Possibility of 3-term mixed chain
if (hpPhase >= 4 && _rand.Next(3) == 0 && config.MaxA >= 15)
{
int a3 = _rand.Next(Math.Max(10, config.MinA), config.MaxA + 1);
int b3 = _rand.Next(2, Math.Max(3, a3 / 2));
int c3 = _rand.Next(1, 6);
int res3 = a3 - b3 + c3;
var chainProblem = new MathProblem
{
DisplayA = $"{a3} - {b3} + {c3}",
Operation = "-",
DisplayB = "",
CorrectAnswer = res3.ToString(),
QuestionText = $"{a3} - {b3} + {c3} = ?",
InputType = config.InputType
};
if (config.InputType == "MultipleChoice")
{
chainProblem.Choices = GenerateNumericChoices(res3, 0, config.MaxA + 10);
}
return chainProblem;
}
int minA = config.MinA;
int maxA = config.MaxA;
int minB = config.MinB;
int maxB = config.MaxB;
if (hpPhase == 1)
{
maxA = Math.Max(minA, minA + (maxA - minA) / 2);
maxB = Math.Max(minB, minB + (maxB - minB) / 2);
}
else if (hpPhase >= 3)
{
minA = Math.Min(maxA, minA + (maxA - minA) / 3);
minB = Math.Min(maxB, minB + (maxB - minB) / 3);
}
int b = _rand.Next(minB, maxB + 1);
int a = _rand.Next(Math.Max(b, minA), maxA + 1);
// In Phase 3-4 with carry, prefer crossing ten (e.g., 14 - 8, 32 - 17)
if (hpPhase >= 3 && config.AllowCarryOver && config.MaxA >= 15)
{
int tries = 0;
while ((a % 10 >= b % 10) && tries < 15)
{
tries++;
b = _rand.Next(minB, maxB + 1);
a = _rand.Next(Math.Max(b, minA), maxA + 1);
}
}
int diff = a - b;
var problem = new MathProblem
@@ -84,19 +195,34 @@ public class MathProblemGenerator : IMathProblemGenerator
return problem;
}
private MathProblem GenerateMultiplication(MathConfig config)
private MathProblem GenerateMultiplication(MathConfig config, int hpPhase)
{
var factors = config.Factors != null && config.Factors.Count > 0
? new List<int>(config.Factors)
: new List<int> { 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int factorA;
if (config.Factors != null && config.Factors.Count > 0)
int minMult = config.MinMultiplier;
int maxMult = config.MaxMultiplier;
if (hpPhase == 1)
{
factorA = config.Factors[_rand.Next(config.Factors.Count)];
var lowerFactors = factors.Where(f => f <= 5).ToList();
factorA = lowerFactors.Any() ? lowerFactors[_rand.Next(lowerFactors.Count)] : factors[_rand.Next(factors.Count)];
maxMult = Math.Min(5, config.MaxMultiplier);
}
else if (hpPhase >= 3)
{
var upperFactors = factors.Where(f => f >= 6).ToList();
factorA = upperFactors.Any() ? upperFactors[_rand.Next(upperFactors.Count)] : factors[_rand.Next(factors.Count)];
minMult = Math.Max(5, config.MinMultiplier);
}
else
{
factorA = _rand.Next(2, 10);
factorA = factors[_rand.Next(factors.Count)];
}
int factorB = _rand.Next(config.MinMultiplier, config.MaxMultiplier + 1);
int factorB = _rand.Next(minMult, maxMult + 1);
int product = factorA * factorB;
var problem = new MathProblem
@@ -117,19 +243,34 @@ public class MathProblemGenerator : IMathProblemGenerator
return problem;
}
private MathProblem GenerateDivision(MathConfig config)
private MathProblem GenerateDivision(MathConfig config, int hpPhase)
{
var factors = config.Factors != null && config.Factors.Count > 0
? new List<int>(config.Factors)
: new List<int> { 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int divisor;
if (config.Factors != null && config.Factors.Count > 0)
int minMult = config.MinMultiplier;
int maxMult = config.MaxMultiplier;
if (hpPhase == 1)
{
divisor = config.Factors[_rand.Next(config.Factors.Count)];
var lower = factors.Where(f => f <= 5).ToList();
divisor = lower.Any() ? lower[_rand.Next(lower.Count)] : factors[_rand.Next(factors.Count)];
maxMult = Math.Min(5, config.MaxMultiplier);
}
else if (hpPhase >= 3)
{
var upper = factors.Where(f => f >= 6).ToList();
divisor = upper.Any() ? upper[_rand.Next(upper.Count)] : factors[_rand.Next(factors.Count)];
minMult = Math.Max(5, config.MinMultiplier);
}
else
{
divisor = _rand.Next(2, 10);
divisor = factors[_rand.Next(factors.Count)];
}
int result = _rand.Next(config.MinMultiplier, config.MaxMultiplier + 1);
int result = _rand.Next(minMult, maxMult + 1);
int dividend = divisor * result;
var problem = new MathProblem
@@ -150,41 +291,370 @@ public class MathProblemGenerator : IMathProblemGenerator
return problem;
}
private MathProblem GenerateComparison(MathConfig config)
private MathProblem GenerateComparison(MathConfig config, int hpPhase)
{
if (hpPhase >= 3)
{
int a = _rand.Next(config.MinA, config.MaxA + 1);
int b = _rand.Next(config.MinB, config.MaxB + 1);
int leftVal = a + b;
string leftStr = $"{a} + {b}";
string answer = a < b ? "<" : (a > b ? ">" : "=");
int rightVal;
string rightStr;
if (hpPhase == 3)
{
rightVal = _rand.Next(Math.Max(1, leftVal - 3), leftVal + 4);
rightStr = rightVal.ToString();
}
else
{
int c = _rand.Next(config.MinA, config.MaxA + 1);
int d = _rand.Next(config.MinB, config.MaxB + 1);
rightVal = c + d;
rightStr = $"{c} + {d}";
if (_rand.Next(3) == 0)
{
rightVal = leftVal;
rightStr = $"{a + 1} + {b - 1}";
}
}
string comp = leftVal < rightVal ? "<" : (leftVal > rightVal ? ">" : "=");
return new MathProblem
{
DisplayA = a.ToString(),
DisplayA = leftStr,
Operation = "?",
DisplayB = b.ToString(),
CorrectAnswer = answer,
QuestionText = $"{a} ? {b}",
DisplayB = rightStr,
CorrectAnswer = comp,
QuestionText = $"{leftStr} ? {rightStr}",
InputType = "MultipleChoice",
Choices = new List<string> { "<", "=", ">" }
};
}
public MathProblem GenerateDefenseTest()
int x = _rand.Next(config.MinA, config.MaxA + 1);
int y = _rand.Next(config.MinB, config.MaxB + 1);
if (hpPhase == 2 && _rand.Next(2) == 0)
{
// Simple True/False defense shield challenge
int a = _rand.Next(1, 10);
int b = _rand.Next(1, 10);
bool isCorrect = _rand.Next(2) == 0;
int displayedResult = isCorrect ? (a + b) : (a + b + (_rand.Next(2) == 0 ? 1 : -1));
if (displayedResult <= 0) displayedResult = a + b + 2;
y = Math.Clamp(x + (_rand.Next(2) == 0 ? 1 : -1), config.MinB, config.MaxB);
}
string answer = x < y ? "<" : (x > y ? ">" : "=");
return new MathProblem
{
DisplayA = $"{a} + {b} = {displayedResult}",
DisplayA = x.ToString(),
Operation = "?",
DisplayB = y.ToString(),
CorrectAnswer = answer,
QuestionText = $"{x} ? {y}",
InputType = "MultipleChoice",
Choices = new List<string> { "<", "=", ">" }
};
}
// ========================================================
// ADVANCED / CHALLENGING RESCUE DEFENSE CHALLENGES
// ========================================================
public MathProblem GenerateDefenseTest(MathConfig config)
{
bool hasComparison = config.Operations.Any(o => o is "<" or ">" or "=");
bool hasMultDiv = config.Operations.Any(o => o is "*" or "/");
if (hasComparison)
{
return GenerateChallengingComparisonDefense(config);
}
else if (hasMultDiv)
{
int variant = _rand.Next(3);
return variant switch
{
0 => GenerateMissingFactorDefense(config),
1 => GenerateMixedMultAddDefense(config),
_ => GenerateTrickyVerificationDefense(config, isMult: true)
};
}
else
{
int variant = _rand.Next(3);
return variant switch
{
0 => GenerateMissingNumberDefense(config),
1 => GenerateThreeTermChainDefense(config),
_ => GenerateTrickyVerificationDefense(config, isMult: false)
};
}
}
private MathProblem GenerateChallengingComparisonDefense(MathConfig config)
{
// Compare expressions: (a + b) ? (c - d)
int valLeft, valRight;
string exprLeft, exprRight;
if (config.MaxA > 100) // Grade 3 large numbers
{
int a = _rand.Next(10, 50) * 10;
int b = _rand.Next(5, 30) * 10;
valLeft = a + b;
exprLeft = $"{a} + {b}";
int c = _rand.Next(20, 80) * 10;
int d = _rand.Next(5, 25) * 10;
valRight = c - d;
exprRight = $"{c} - {d}";
}
else if (config.MaxSum > 20 || config.MaxA > 20) // Grade 2
{
int a = _rand.Next(10, 45);
int b = _rand.Next(5, 30);
valLeft = a + b;
exprLeft = $"{a} + {b}";
int c = _rand.Next(30, 80);
int d = _rand.Next(5, 25);
valRight = c - d;
exprRight = $"{c} - {d}";
}
else // Grade 1
{
int a = _rand.Next(2, 9);
int b = _rand.Next(2, 9);
valLeft = a + b;
exprLeft = $"{a} + {b}";
int c = _rand.Next(6, 18);
int d = _rand.Next(1, 6);
valRight = c - d;
exprRight = $"{c} - {d}";
}
// Sometimes create equal expressions
if (_rand.Next(3) == 0)
{
valRight = valLeft;
int offset = _rand.Next(1, 8);
exprRight = $"{valLeft + offset} - {offset}";
}
string answer = valLeft < valRight ? "<" : (valLeft > valRight ? ">" : "=");
return new MathProblem
{
DisplayA = exprLeft,
Operation = "?",
DisplayB = exprRight,
CorrectAnswer = answer,
QuestionText = $"Porovnaj výrazy:\n( {exprLeft} ) ? ( {exprRight} )",
InputType = "MultipleChoice",
Choices = new List<string> { "<", "=", ">" },
IsDefenseTest = true
};
}
private MathProblem GenerateMissingNumberDefense(MathConfig config)
{
bool isAdd = _rand.Next(2) == 0;
int missing, known, result;
string equation;
if (config.MaxSum > 100 || config.MaxA > 100) // Grade 3
{
known = _rand.Next(10, 50) * 10;
missing = _rand.Next(10, 40) * 10;
if (isAdd)
{
result = known + missing;
equation = _rand.Next(2) == 0 ? $"{known} + ❓ = {result}" : $"❓ + {known} = {result}";
}
else
{
result = known;
int total = known + missing;
equation = $"{total} - ❓ = {result}";
}
}
else if (config.MaxSum > 20 || config.MaxA > 20) // Grade 2
{
known = _rand.Next(12, 50);
missing = _rand.Next(8, 35);
if (isAdd)
{
result = known + missing;
equation = _rand.Next(2) == 0 ? $"{known} + ❓ = {result}" : $"❓ + {known} = {result}";
}
else
{
result = known;
int total = known + missing;
equation = $"{total} - ❓ = {result}";
}
}
else // Grade 1
{
known = _rand.Next(3, 10);
missing = _rand.Next(3, 10);
if (isAdd)
{
result = known + missing;
equation = _rand.Next(2) == 0 ? $"{known} + ❓ = {result}" : $"❓ + {known} = {result}";
}
else
{
result = known;
int total = known + missing;
equation = $"{total} - ❓ = {result}";
}
}
return new MathProblem
{
DisplayA = equation,
Operation = "",
DisplayB = "",
CorrectAnswer = missing.ToString(),
QuestionText = $"Doplň chýbajúce číslo ❓:\n{equation}",
InputType = "MultipleChoice",
Choices = GenerateNumericChoices(missing, 1, 1000),
IsDefenseTest = true
};
}
private MathProblem GenerateThreeTermChainDefense(MathConfig config)
{
int a, b, c, result;
string expr;
if (config.MaxSum > 100 || config.MaxA > 100) // Grade 3
{
a = _rand.Next(15, 45) * 10;
b = _rand.Next(10, 30) * 10;
c = _rand.Next(5, 20) * 10;
result = a + b - c;
expr = $"{a} + {b} - {c}";
}
else if (config.MaxSum > 20 || config.MaxA > 20) // Grade 2
{
a = _rand.Next(15, 50);
b = _rand.Next(10, 30);
c = _rand.Next(5, 20);
result = a + b - c;
expr = $"{a} + {b} - {c}";
}
else // Grade 1
{
a = _rand.Next(4, 9);
b = _rand.Next(3, 8);
c = _rand.Next(2, 6);
result = a + b - c;
expr = $"{a} + {b} - {c}";
}
return new MathProblem
{
DisplayA = expr,
Operation = "",
DisplayB = "",
CorrectAnswer = result.ToString(),
QuestionText = $"Vypočítaj trojčlenný reťazec:\n{expr} = ?",
InputType = "MultipleChoice",
Choices = GenerateNumericChoices(result, 0, 1000),
IsDefenseTest = true
};
}
private MathProblem GenerateMissingFactorDefense(MathConfig config)
{
int factorA;
if (config.Factors != null && config.Factors.Count > 0)
{
factorA = config.Factors[_rand.Next(config.Factors.Count)];
}
else
{
factorA = _rand.Next(2, 10);
}
int missing = _rand.Next(2, 10);
int product = factorA * missing;
bool isDiv = _rand.Next(2) == 0;
string equation = isDiv
? $"{product} ÷ ❓ = {factorA}"
: (_rand.Next(2) == 0 ? $"{factorA} × ❓ = {product}" : $"❓ × {factorA} = {product}");
return new MathProblem
{
DisplayA = equation,
Operation = "",
DisplayB = "",
CorrectAnswer = missing.ToString(),
QuestionText = $"Nájdi neznáme číslo ❓:\n{equation}",
InputType = "MultipleChoice",
Choices = GenerateNumericChoices(missing, 1, 10),
IsDefenseTest = true
};
}
private MathProblem GenerateMixedMultAddDefense(MathConfig config)
{
int f1 = _rand.Next(2, 8);
int f2 = _rand.Next(2, 6);
int prod = f1 * f2;
int add = _rand.Next(4, 16);
bool isSub = _rand.Next(2) == 0 && prod > add;
int result = isSub ? prod - add : prod + add;
string expr = isSub ? $"{f1} × {f2} - {add}" : $"{f1} × {f2} + {add}";
return new MathProblem
{
DisplayA = expr,
Operation = "",
DisplayB = "",
CorrectAnswer = result.ToString(),
QuestionText = $"Kombinovaný výpočet (najprv násob!):\n{expr} = ?",
InputType = "MultipleChoice",
Choices = GenerateNumericChoices(result, 1, 100),
IsDefenseTest = true
};
}
private MathProblem GenerateTrickyVerificationDefense(MathConfig config, bool isMult)
{
bool isCorrect = _rand.Next(2) == 0;
string equation;
if (isMult)
{
int a = _rand.Next(3, 10);
int b = _rand.Next(3, 10);
int prod = a * b;
int displayed = isCorrect ? prod : (prod + (_rand.Next(2) == 0 ? _rand.Next(1, 4) : -_rand.Next(1, 4)));
if (displayed <= 0) displayed = prod + 2;
equation = $"{a} × {b} = {displayed}";
}
else
{
int a = _rand.Next(15, 45);
int b = _rand.Next(15, 45);
int sum = a + b;
int offset = _rand.Next(2) == 0 ? (_rand.Next(2) == 0 ? 1 : -1) : (_rand.Next(2) == 0 ? 10 : -10);
int displayed = isCorrect ? sum : (sum + offset);
if (displayed <= 0) displayed = sum + 2;
equation = $"{a} + {b} = {displayed}";
}
return new MathProblem
{
DisplayA = equation,
Operation = "",
DisplayB = "",
CorrectAnswer = isCorrect ? "ÁNO" : "NIE",
QuestionText = $"Je to správne?\n{a} + {b} = {displayedResult}",
QuestionText = $"Je táto rovnica správna?\n{equation}",
InputType = "MultipleChoice",
Choices = new List<string> { "ÁNO", "NIE" },
IsDefenseTest = true
@@ -194,13 +664,22 @@ public class MathProblemGenerator : IMathProblemGenerator
private List<string> GenerateNumericChoices(int correct, int min, int max)
{
var set = new HashSet<int> { correct };
while (set.Count < 3)
int tries = 0;
while (set.Count < 4 && tries < 30)
{
int offset = _rand.Next(-5, 6);
if (offset == 0) offset = 1;
tries++;
int offset = _rand.Next(-4, 5);
if (offset == 0) offset = _rand.Next(2) == 0 ? 1 : -1;
int candidate = Math.Clamp(correct + offset, min, max);
set.Add(candidate);
}
int fallback = 1;
while (set.Count < 4)
{
set.Add(Math.Max(min, correct + fallback));
fallback++;
}
return set.OrderBy(_ => _rand.Next()).Select(x => x.ToString()).ToList();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 884 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 859 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 736 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 739 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 780 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 781 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 780 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 782 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 752 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 752 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 752 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

After

Width:  |  Height:  |  Size: 753 KiB

Some files were not shown because too many files have changed in this diff Show More