Skip to content

Commit

Permalink
Provide several fixes. Support outcomes calculation in question for a…
Browse files Browse the repository at this point in the history
…ll with answer options and complex answer
  • Loading branch information
VladimirKhil committed Dec 16, 2024
1 parent 9c6f488 commit 0d49c34
Show file tree
Hide file tree
Showing 18 changed files with 122 additions and 67 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Company>Khil-soft</Company>
<Copyright>Copyright © Khil-soft 2002 - 2024</Copyright>
<SIGameVersion>7.13.2</SIGameVersion>
<SIQuesterVersion>6.2.1</SIQuesterVersion>
<SIQuesterVersion>6.2.2</SIQuesterVersion>
<SImulatorVersion>3.1.0</SImulatorVersion>
</PropertyGroup>

Expand Down
10 changes: 4 additions & 6 deletions src/Common/SIEngine.Core/QuestionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public sealed class QuestionEngine

public bool CanNext => _script != null && _stepIndex < _script.Steps.Count;

public string QuestionTypeName { get; } = "";

/// <summary>
/// Initializes a new instance of <see cref="QuestionEngine" /> class.
/// </summary>
Expand All @@ -41,12 +43,8 @@ public QuestionEngine(Question question, QuestionEngineOptions options, IQuestio

if (_script == null)
{
var typeName = _question.TypeName == QuestionTypes.Default ? options.DefaultTypeName : _question.TypeName;

// TODO: do not update package objects; they should be read only
question.TypeName = typeName;

ScriptsLibrary.Scripts.TryGetValue(typeName, out _script);
QuestionTypeName = _question.TypeName == QuestionTypes.Default || !options.PlaySpecials ? options.DefaultTypeName : _question.TypeName;
ScriptsLibrary.Scripts.TryGetValue(QuestionTypeName, out _script);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/Common/SIEngine.Core/QuestionEngineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ public sealed class QuestionEngineOptions
/// Default type name.
/// </summary>
public string DefaultTypeName { get; set; } = QuestionTypes.Simple;

/// <summary>
/// Play special questions.
/// </summary>
public bool PlaySpecials { get; set; } = true;
}
6 changes: 2 additions & 4 deletions src/Common/SIEngine/EngineBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ protected set
public event Action<IEnumerable<string>>? GameThemes;

// TODO: investigate and eliminate duplicates
public event Action? QuestionPostInfo;
public event Action<int, int>? EndQuestion;
public event Action? QuestionFinish;
public event Action? NextQuestion;
Expand Down Expand Up @@ -210,8 +209,6 @@ protected EngineBase(

protected void OnGameThemes(IEnumerable<string> gameThemes) => GameThemes?.Invoke(gameThemes);

protected void OnQuestionPostInfo() => QuestionPostInfo?.Invoke();

protected void OnQuestionFinish() => QuestionFinish?.Invoke();

protected void OnEndQuestion(int themeIndex, int questionIndex) => EndQuestion?.Invoke(themeIndex, questionIndex);
Expand Down Expand Up @@ -341,7 +338,7 @@ protected void OnQuestion()
{
if (!QuestionEngine.PlayNext())
{
OnQuestionPostInfo();
PlayHandler.OnQuestionEnd();
Stage = GameStage.EndQuestion;
}
}
Expand All @@ -359,6 +356,7 @@ protected void OnMoveToQuestion()
: FalseStartMode.Disabled,

ShowSimpleRightAnswers = options.ShowRight,
PlaySpecials = options.PlaySpecials,

DefaultTypeName = GameRules.GetRulesForRoundType(ActiveRound.Type).DefaultQuestionType
});
Expand Down
11 changes: 3 additions & 8 deletions src/Common/SIEngine/GameEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,9 @@ public override void MoveNext()

private void OnQuestionType()
{
var isDefault = ActiveQuestion.TypeName == GameRules.GetRulesForRoundType(ActiveRound.Type).DefaultQuestionType;
PlayHandler.OnQuestionType(ActiveQuestion.TypeName, isDefault);
var questionTypeName = QuestionEngine.QuestionTypeName;
var isDefault = questionTypeName == GameRules.GetRulesForRoundType(ActiveRound.Type).DefaultQuestionType;
PlayHandler.OnQuestionType(questionTypeName, isDefault);
Stage = GameStage.Question;
}

Expand All @@ -172,12 +173,6 @@ private void SelectQuestion(int themeIndex, int questionIndex)
_questionIndex = questionIndex;

SetActiveThemeQuestion();

if (!OptionsProvider().PlaySpecials)
{
ActiveQuestion.TypeName = QuestionTypes.Default;
}

OnMoveToQuestion();
UpdateCanNext();
CanMoveBack = SelectionStrategy.CanMoveBack();
Expand Down
5 changes: 5 additions & 0 deletions src/Common/SIEngine/ISIEnginePlayHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,9 @@ public interface ISIEnginePlayHandler
/// <param name="questionIndex">Question index.</param>
/// <param name="price">Question price.</param>
void OnQuestionRestored(int themeIndex, int questionIndex, int price);

/// <summary>
/// Handles question end.
/// </summary>
void OnQuestionEnd();
}
110 changes: 69 additions & 41 deletions src/SICore/SICore/Clients/Game/GameLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ internal void Run()
Engine.Package += Engine_Package;
Engine.GameThemes += Engine_GameThemes;

Engine.QuestionPostInfo += Engine_QuestionPostInfo;
Engine.QuestionFinish += Engine_QuestionFinish;
Engine.NextQuestion += Engine_NextQuestion;

Expand Down Expand Up @@ -286,7 +285,7 @@ internal void ProcessApellationRequest()
}
}

private void Engine_QuestionPostInfo()
private void OnQuestionPostInfo()
{
_tasksHistory.AddLogEntry("Engine_QuestionPostInfo: Appellation activated");

Expand Down Expand Up @@ -1891,8 +1890,8 @@ public void ExecuteTask(Tasks task, int arg)
AnnounceStake();
break;

case Tasks.AnnouncePostStake:
AnnouncePostStake();
case Tasks.AnnouncePostStakeWithAnswerOptions:
AnnouncePostStakeWithAnswerOptions();
break;

case Tasks.WaitReport:
Expand Down Expand Up @@ -2496,11 +2495,11 @@ private void WaitReport()
}
}

private void AnnouncePostStake()
private void AnnouncePostStakeWithAnswerOptions()
{
if (_data.AnnouncedAnswerersEnumerator == null || !_data.AnnouncedAnswerersEnumerator.MoveNext())
{
ScheduleExecution(Tasks.MoveNext, 15, 1, true);
OnQuestionPostInfo();
return;
}

Expand All @@ -2509,7 +2508,7 @@ private void AnnouncePostStake()

_data.PlayerIsRight = _data.Answerer?.Answer == _data.RightOptionLabel;
AnnounceStakeCore();
ScheduleExecution(Tasks.AnnouncePostStake, 15);
ScheduleExecution(Tasks.AnnouncePostStakeWithAnswerOptions, 15);
}

private void AnnounceStake()
Expand Down Expand Up @@ -2960,7 +2959,7 @@ internal void Announce()

_gameActions.PlayerReplic(answererIndex, answer);

if (_data.QuestionPlayState.AnswerOptions != null)
if (_data.QuestionPlayState.ValidateAfterRightAnswer)
{
ScheduleExecution(Tasks.Announce, 25, force: true);
}
Expand Down Expand Up @@ -4700,16 +4699,17 @@ internal void OnSimpleAnswer(string answer)
internal void OnComplexAnswer()
{
var last = _data.QuestionHistory.LastOrDefault();
var answer = _data.Question?.Right.FirstOrDefault();
var answer = _data.Question?.Right.FirstOrDefault() ?? "";

if (last == null || !last.IsRight) // There has been no right answer
{
var printedAnswer = answer != null ? $"{LO[nameof(R.RightAnswer)]}: {answer}" : LO[nameof(R.RightAnswerInOnTheScreen)];
var printedAnswer = answer.Length > 0 ? $"{LO[nameof(R.RightAnswer)]}: {answer}" : LO[nameof(R.RightAnswerInOnTheScreen)];
_gameActions.ShowmanReplic(printedAnswer);
}

if (_data.QuestionPlayState.AnswerOptions != null)
{
_data.RightOptionLabel = answer;
var answerIndex = Array.FindIndex(_data.QuestionPlayState.AnswerOptions, o => o.Label == answer);

if (answerIndex > -1)
Expand All @@ -4718,63 +4718,91 @@ internal void OnComplexAnswer()
}
}

_gameActions.SendMessageWithArgs(Messages.RightAnswerStart, ContentTypes.Text, answer ?? "");
_gameActions.SendMessageWithArgs(Messages.RightAnswerStart, ContentTypes.Text, answer);
}

internal void OnRightAnswerOption(string rightOptionLabel)
{
_data.RightOptionLabel = rightOptionLabel;
_gameActions.SendMessageWithArgs(Messages.RightAnswer, ContentTypes.Text, rightOptionLabel);
var answerTime = _data.Settings.AppSettings.TimeSettings.TimeForRightAnswer;
answerTime = (answerTime == 0 ? 2 : answerTime) * 10;
ScheduleExecution(Tasks.MoveNext, answerTime);
}

internal void OnQuestionEnd()
{
if (_data.QuestionPlayState.ValidateAfterRightAnswer)
{
if (ValidatePlayersAnswers())
{
return;
}
}

OnQuestionPostInfo();
}

private bool ValidatePlayersAnswers()
{
if (HaveMultipleAnswerers() && _data.AnnouncedAnswerersEnumerator != null)
{
_data.AnnouncedAnswerersEnumerator.Reset();

if (_data.QuestionPlayState.HiddenStakes)
{
ScheduleExecution(Tasks.AnnouncePostStake, (answerTime == 0 ? 2 : answerTime) * 10);
return;
ScheduleExecution(Tasks.AnnouncePostStakeWithAnswerOptions, 1);
return true;
}
else
{
while (_data.AnnouncedAnswerersEnumerator.MoveNext())
{
var answererIndex = _data.AnnouncedAnswerersEnumerator.Current;
CalculateOutcomesByRightAnswerOption();
}
}

if (answererIndex < 0 || answererIndex >= _data.Players.Count)
{
continue;
}
return false;
}

var answerer = _data.Players[answererIndex];
var isRight = answerer.Answer == _data.RightOptionLabel;
private void CalculateOutcomesByRightAnswerOption()
{
if (_data.AnnouncedAnswerersEnumerator == null)
{
return;
}

var message = new MessageBuilder(Messages.Person);
int outcome;
while (_data.AnnouncedAnswerersEnumerator.MoveNext())
{
var answererIndex = _data.AnnouncedAnswerersEnumerator.Current;

if (isRight)
{
message.Add('+');
answerer.AddRightSum(_data.CurPriceRight);
outcome = _data.CurPriceRight;
}
else
{
message.Add('-');
answerer.SubtractWrongSum(_data.CurPriceWrong);
outcome = _data.CurPriceWrong;
}
if (answererIndex < 0 || answererIndex >= _data.Players.Count)
{
continue;
}

message.Add(answererIndex).Add(outcome);
_gameActions.SendMessage(message.ToString());
}
var answerer = _data.Players[answererIndex];
var isRight = answerer.Answer == _data.RightOptionLabel;

_gameActions.InformSums();
var message = new MessageBuilder(Messages.Person);
int outcome;

if (isRight)
{
message.Add('+');
answerer.AddRightSum(_data.CurPriceRight);
outcome = _data.CurPriceRight;
}
else
{
message.Add('-');
answerer.SubtractWrongSum(_data.CurPriceWrong);
outcome = _data.CurPriceWrong;
}

message.Add(answererIndex).Add(outcome);
_gameActions.SendMessage(message.ToString());
}

ScheduleExecution(Tasks.MoveNext, (answerTime == 0 ? 2 : answerTime) * 10);
_gameActions.InformSums();
}

internal void OnAnnouncePrice(NumberSet availableRange)
Expand Down
2 changes: 2 additions & 0 deletions src/SICore/SICore/Clients/Game/PlayHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,6 @@ public void OnQuestionRestored(int themeIndex, int questionIndex, int price)
question.Price = price;
GameActions?.SendMessageWithArgs(Messages.Toggle, themeIndex, questionIndex, price);
}

public void OnQuestionEnd() => GameLogic?.OnQuestionEnd();
}
5 changes: 5 additions & 0 deletions src/SICore/SICore/Clients/Game/QuestionPlayState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ internal sealed class QuestionPlayState
/// </summary>
internal Dictionary<string, (bool, double)?> Validations { get; } = new();

/// <summary>
/// Should the player answers be validated after right answer.
/// </summary>
internal bool ValidateAfterRightAnswer => AnswerOptions != null;

internal void Clear()
{
AnswererIndicies.Clear();
Expand Down
4 changes: 2 additions & 2 deletions src/SICore/SICore/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ public enum Tasks
/// Объявление ставки игрока в финале
/// </summary>
AnnounceStake,

/// <summary>
/// Announce stake after right answer.
/// </summary>
AnnouncePostStake,
AnnouncePostStakeWithAnswerOptions,

/// <summary>
/// Завершение игры
Expand Down
3 changes: 2 additions & 1 deletion src/SICore/SICore/Services/Intelligence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ internal sealed class Intelligence : IIntelligence
throw new InvalidOperationException("Game table is empty");
}

var maxQuestionCount = table.Max(theme => theme.Questions.Count(QuestionHelper.IsActive));
// Do not filter by IsActive as we'll miss the questions after the empty ones
var maxQuestionCount = table.Max(theme => theme.Questions.Count);

if (maxQuestionCount == 0)
{
Expand Down
2 changes: 1 addition & 1 deletion src/SIGame/SIGame.ViewModel/ViewModel/GameViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public sealed class GameViewModel : IAsyncDisposable, INotifyPropertyChanged
public IViewerClient? Host
{
get => _host;
set => _host = value;
set { _host = value; UpdateCommands(); }
}

private readonly ViewerData _data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@ public void OnInfo()
{
Greet();
}

if (!_data.IsNetworkGame && _gameViewModel.Ready.CanBeExecuted)
{
_gameViewModel.Ready.Execute(null);
}
}

private async void Greet()
Expand Down
Loading

0 comments on commit 0d49c34

Please sign in to comment.