Skip to content

Commit 82dba51

Browse files
committed
Reworked and slimmed-out GamePageViewModel.
Replaced GameMode with messaegs being sent. Removed options and unnecessary service dependencies.
1 parent bdcaf01 commit 82dba51

1 file changed

Lines changed: 84 additions & 231 deletions

File tree

Lines changed: 84 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -1,272 +1,125 @@
1-
using Codebreaker.ViewModels.Components;
2-
using Codebreaker.ViewModels.Contracts.Services;
3-
using Microsoft.Extensions.Options;
1+
using Codebreaker.ViewModels.Contracts.Services;
2+
using Codebreaker.ViewModels.Messages;
3+
using System.ComponentModel;
4+
using System.Net;
45

56
namespace Codebreaker.ViewModels;
67

7-
public enum GameMode
8+
public partial class GamePageViewModel(IGamesClient gamesClient, IInfoBarService infoBarService) : ObservableRecipient
89
{
9-
NotRunning,
10-
Started,
11-
MoveSet,
12-
Lost,
13-
Won
14-
}
15-
16-
public enum GameMoveValue
17-
{
18-
Started,
19-
Completed
20-
}
21-
22-
/// <summary>
23-
/// Configure to enable dialogs (via <see cref="IDialogService"/>), or use the <see cref="InfoBarService"/>.
24-
/// </summary>
25-
public class GamePageViewModelOptions
26-
{
27-
}
28-
29-
public partial class GamePageViewModel : ObservableObject
30-
{
31-
private readonly IGamesClient _client;
32-
private int _moveNumber = 0;
33-
34-
private readonly IDialogService _dialogService;
35-
private readonly IInfoBarService _infoBarService;
36-
37-
public GamePageViewModel(
38-
IGamesClient client,
39-
IOptions<GamePageViewModelOptions> options,
40-
IDialogService dialogService,
41-
IInfoBarService infoBarService
42-
)
43-
{
44-
_client = client;
45-
_dialogService = dialogService;
46-
_infoBarService = infoBarService;
47-
48-
PropertyChanged += (sender, e) =>
49-
{
50-
if (e.PropertyName == nameof(GameStatus))
51-
WeakReferenceMessenger.Default.Send(new GameStateChangedMessage(GameStatus));
52-
};
53-
}
54-
55-
private Game? _game;
56-
/// <summary>
57-
/// <see cref="Models.Game"/> instance."/>
58-
/// </summary>
59-
public Game? Game
60-
{
61-
get => _game;
62-
set
63-
{
64-
OnPropertyChanging(nameof(Game));
65-
OnPropertyChanging(nameof(Fields));
66-
_game = value;
67-
68-
Fields.Clear();
69-
70-
for (int i = 0; i < _game?.NumberCodes; i++)
71-
{
72-
SelectedFieldViewModel field = new();
73-
field.PropertyChanged += (sender, e) => SetMoveCommand.NotifyCanExecuteChanged();
74-
Fields.Add(field);
75-
}
76-
77-
OnPropertyChanged(nameof(Game));
78-
OnPropertyChanged(nameof(Fields));
79-
}
80-
}
81-
82-
[ObservableProperty]
83-
private string _name = string.Empty;
84-
85-
[NotifyPropertyChangedFor(nameof(IsNameEnterable))]
8610
[ObservableProperty]
87-
private bool _isNamePredefined = false;
88-
89-
public ObservableCollection<SelectedFieldViewModel> Fields { get; } = [];
11+
private bool _isLoading;
9012

91-
public ObservableCollection<SelectionAndKeyPegs> GameMoves { get; } = [];
92-
93-
/// <summary>
94-
/// Status of the game. See <see cref="GameMode"/>.
95-
/// </summary>
9613
[ObservableProperty]
97-
private GameMode _gameStatus = GameMode.NotRunning;
14+
private Game? _game;
9815

99-
/// <summary>
100-
/// An API call to the games-API is in-progress. Use to display progress indicators.
101-
/// </summary>
102-
[NotifyPropertyChangedFor(nameof(IsNameEnterable))]
10316
[ObservableProperty]
104-
private bool _inProgress = false;
17+
private Field[] _selectedFields = [];
10518

10619
[ObservableProperty]
107-
private bool _isCancelling = false;
20+
[NotifyCanExecuteChangedFor(nameof(StartGameCommand))]
21+
private string _username = string.Empty;
10822

109-
/// <summary>
110-
/// The player name can be entered.
111-
/// </summary>
112-
public bool IsNameEnterable => !InProgress && !IsNamePredefined;
23+
private bool CanStartGame() => Username is not null && Username.Length > 2;
11324

114-
/// <summary>
115-
/// Starts a new game using <see cref="IGamesClient"/>.
116-
/// Updates the <see cref="GameStatus"/> property.
117-
/// Initializes <see cref="Game"/>).
118-
/// Increments the move number.
119-
/// Shows <see cref="IDialogService"/> messages or <see cref="_infoBarService"/> messages with errors.
120-
/// </summary>
121-
/// <returns>A task</returns>
122-
[RelayCommand(AllowConcurrentExecutions = false, FlowExceptionsToTaskScheduler = true)]
123-
private async Task StartGameAsync()
25+
[RelayCommand(CanExecute = nameof(CanStartGame))]
26+
private async Task StartGameAsync(CancellationToken cancellationToken)
12427
{
28+
IsLoading = true;
29+
var usedGameMode = GameType.Game6x4;
30+
(Guid id, int numberCode, int maxMoves, IDictionary<string, string[]> fieldValues) response;
31+
12532
try
12633
{
127-
InitializeValues();
128-
129-
InProgress = true;
130-
(Guid gameId, int numberCodes, int maxMoves, IDictionary<string, string[]> fieldValues) = await _client.StartGameAsync(GameType.Game6x4, Name);
131-
132-
GameStatus = GameMode.Started;
133-
134-
Game = new Game(gameId, GameType.Game6x4, Name, DateTime.Now, numberCodes, maxMoves)
135-
{
136-
FieldValues = fieldValues
137-
};
138-
_moveNumber++;
34+
response = await gamesClient.StartGameAsync(usedGameMode, Username);
13935
}
140-
catch (Exception ex)
36+
catch (InvalidOperationException)
14137
{
142-
_infoBarService.New
143-
.IsErrorMessage()
144-
.WithMessage(ex.Message)
145-
.WithAction((message) =>
146-
{
147-
GameStatus = GameMode.NotRunning;
148-
message.Close();
149-
})
150-
.Show();
38+
infoBarService.New.WithMessage("Invalid operation").Show();
39+
return;
40+
}
41+
catch(HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest)
42+
{
43+
infoBarService.New.WithMessage(ex.Message).Show();
44+
return;
45+
}
46+
catch (HttpRequestException)
47+
{
48+
infoBarService.New.WithMessage("Networking issues").Show();
49+
return;
15150
}
15251
finally
15352
{
154-
InProgress = false;
53+
IsLoading = false;
15554
}
156-
}
15755

158-
// TODO: end of the game is not yet implemented (in the client library)
159-
// [RelayCommand(AllowConcurrentExecutions = false, FlowExceptionsToTaskScheduler = true)]
160-
// private async Task CancelGameAsync()
161-
// {
162-
// if (Game is null)
163-
// throw new InvalidOperationException("No game running");
56+
Game = new Game(response.id, usedGameMode, Username, DateTime.Now, response.numberCode, response.maxMoves, response.fieldValues);
16457

165-
// IsCancelling = true;
58+
WeakReferenceMessenger.Default.Send(new GameStartedMessage(Game));
16659

167-
// try
168-
// {
169-
//// await _client.CancelGameAsync(Game!.Value.GameId);
170-
// GameStatus = GameMode.NotRunning;
171-
// }
172-
// catch (Exception ex)
173-
// {
174-
// InfoBarMessageService.ShowError(ex.Message);
60+
// Initialize SelectedFields
61+
SelectedFields = Enumerable.Range(0, Game.NumberCodes)
62+
.Select(i =>
63+
{
64+
var field = new Field(Game.FieldValues["colors"]); // TODO: Hardcoding "colors" is not suitable for all game types
65+
field.PropertyChanged += (object? sender, PropertyChangedEventArgs args) => MakeMoveCommand.NotifyCanExecuteChanged();
66+
return field;
67+
})
68+
.ToArray();
69+
}
17570

176-
// if (_enableDialogs)
177-
// await _dialogService.ShowMessageAsync(ex.Message);
178-
// }
179-
// finally
180-
// {
181-
// IsCancelling = false;
182-
// }
183-
// }
71+
private bool CanMakeMove() => SelectedFields.All(field => field is not null);
18472

185-
/// <summary>
186-
///
187-
/// </summary>
188-
/// <returns>A task</returns>
189-
/// <exception cref="InvalidOperationException"></exception>
190-
[RelayCommand(CanExecute = nameof(CanSetMove), AllowConcurrentExecutions = false, FlowExceptionsToTaskScheduler = true)]
191-
private async Task SetMoveAsync()
73+
[RelayCommand(CanExecute = nameof(CanMakeMove))]
74+
private async Task MakeMoveAsync(CancellationToken cancellationToken)
19275
{
193-
try
194-
{
195-
InProgress = true;
196-
WeakReferenceMessenger.Default.Send(new GameMoveMessage(GameMoveValue.Started));
197-
198-
if (_game is null)
199-
throw new InvalidOperationException("no game running");
200-
201-
if (Fields.Count != _game.NumberCodes || Fields.Any(x => !x.IsSet))
202-
throw new InvalidOperationException("all colors need to be selected before invoking this method");
203-
204-
string[] guessPegs = Fields.Select(x => x.Value!).ToArray();
76+
if (Game is null)
77+
throw new InvalidOperationException("A game needs to be started before making a move");
20578

206-
(string[] keyPegs, bool ended, bool isVictory) = await _client.SetMoveAsync(_game.GameId, Name, GameType.Game6x4, _moveNumber, guessPegs);
79+
var selectedColors = SelectedFields
80+
.Select(x => x.Color)
81+
.ToArray();
20782

208-
SelectionAndKeyPegs selectionAndKeyPegs = new(guessPegs, keyPegs, _moveNumber++);
209-
GameMoves.Add(selectionAndKeyPegs);
210-
GameStatus = GameMode.MoveSet;
83+
if (selectedColors.Any(color => color is null))
84+
throw new InvalidOperationException("All colors need to be selected before making a move");
21185

212-
WeakReferenceMessenger.Default.Send(new GameMoveMessage(GameMoveValue.Completed, selectionAndKeyPegs));
86+
WeakReferenceMessenger.Default.Send(new MakeMoveMessage(new(selectedColors!)));
21387

214-
if (isVictory)
215-
{
216-
GameStatus = GameMode.Won;
217-
_infoBarService.New.IsSuccessMessage().WithMessage("Congratulations - you won!").Show();
218-
}
219-
else if (ended)
220-
{
221-
GameStatus = GameMode.Lost;
222-
_infoBarService.New.WithMessage("Sorry, you didn't find the matching colors!").Show();
223-
}
88+
IsLoading = true;
89+
(string[] keyPegs, bool hasEnded, bool isVictory) response;
90+
try
91+
{
92+
response = await gamesClient.SetMoveAsync(Game.Id, Game.PlayerName, GameType.Game6x4, Game.Moves.Count + 1, selectedColors!);
22493
}
225-
catch (Exception ex)
94+
catch (InvalidOperationException)
22695
{
227-
_infoBarService.New.WithMessage(ex.Message).IsErrorMessage().Show();
96+
infoBarService.New.WithMessage("Invalid operation").Show();
97+
return;
98+
}
99+
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest)
100+
{
101+
infoBarService.New.WithMessage(ex.Message).Show();
102+
return;
103+
}
104+
catch (HttpRequestException)
105+
{
106+
infoBarService.New.WithMessage("Networking issues").Show();
107+
return;
228108
}
229109
finally
230110
{
231-
ClearSelectedColor();
232-
InProgress = false;
111+
IsLoading = false;
233112
}
234-
}
235-
236-
private bool CanSetMove =>
237-
Fields.All(s => s is not null && s.IsSet);
238113

239-
private void ClearSelectedColor()
240-
{
241-
for (int i = 0; i < Fields.Count; i++)
242-
Fields[i].Reset();
114+
var newMove = new Move(selectedColors!, response.keyPegs);
115+
Game.Moves.Add(newMove);
116+
WeakReferenceMessenger.Default.Send(new MakeMoveMessage(newMove));
243117

244-
SetMoveCommand.NotifyCanExecuteChanged();
245-
}
246-
247-
private void InitializeValues()
248-
{
249-
ClearSelectedColor();
250-
GameMoves.Clear();
251-
GameStatus = GameMode.NotRunning;
252-
_infoBarService.Clear();
253-
_moveNumber = 0;
118+
if (response.hasEnded)
119+
{
120+
Game.EndTime = DateTime.Now;
121+
Game.IsVictory = response.isVictory;
122+
WeakReferenceMessenger.Default.Send(new GameEndedMessage(Game));
123+
}
254124
}
255-
}
256-
257-
/// <summary>
258-
///
259-
/// </summary>
260-
/// <param name="GuessPegs">String representation of guesses</param>
261-
/// <param name="KeyPegs">String representation of results</param>
262-
/// <param name="MoveNumber">The move number</param>
263-
public record SelectionAndKeyPegs(string[] GuessPegs, string[] KeyPegs, int MoveNumber);
264-
265-
public record class GameStateChangedMessage(GameMode GameMode);
266-
267-
/// <summary>
268-
/// Messages sent when the games starts, or a move is set.
269-
/// </summary>
270-
/// <param name="GameMoveValue"><see cref="GameMoveValue"/></param>
271-
/// <param name="SelectionAndKeyPegs"><see cref="SelectionAndKeyPegs"/></param>
272-
public record class GameMoveMessage(GameMoveValue GameMoveValue, SelectionAndKeyPegs? SelectionAndKeyPegs = null);
125+
}

0 commit comments

Comments
 (0)