From c7d3888a7032424fe78579c5d0e983a43baef379 Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Tue, 20 Sep 2022 18:37:32 +0200 Subject: [PATCH 1/6] :bug: Change HashSet to List for ordering, add order --- Sources/Model/Players/PlayerManager.cs | 90 +++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/Sources/Model/Players/PlayerManager.cs b/Sources/Model/Players/PlayerManager.cs index f89626a..273d6de 100644 --- a/Sources/Model/Players/PlayerManager.cs +++ b/Sources/Model/Players/PlayerManager.cs @@ -7,11 +7,16 @@ namespace Model.Players public class PlayerManager : IManager { /// - /// a hashset of the players that this manager is in charge of + /// a collection of the players that this manager is in charge of + /// + private readonly List players; + + /// + /// references the position in list of the current player, for a given game. ///
- /// each player is unique (set), and the hash is based on the player's values (name) + /// ASSUMING that each Game made its own instance of PlayerManager ///
- private readonly HashSet players; + public int NextIndex { get; private set; } = 0; public PlayerManager() { @@ -24,12 +29,16 @@ namespace Model.Players /// added player, or null if was null public Player Add(Player toAdd) { - if (toAdd != null) + if (toAdd is null) + { + throw new ArgumentNullException(nameof(toAdd), "param should not be null"); + } + if (players.Contains(toAdd)) { - players.Add(toAdd); - return toAdd; + throw new ArgumentException("this username is already taken", nameof(toAdd)); } - throw new ArgumentNullException(nameof(toAdd), "param should not be null"); + players.Add(toAdd); + return toAdd; } /// @@ -68,6 +77,71 @@ namespace Model.Players /// a readonly enumerable of all this manager's players public IEnumerable GetAll() => players.AsEnumerable(); + /// + /// finds and returns the player whose turn it is + ///
+ /// SHOULD HAVE NO SIDE EFFECT + ///
+ /// + /// + + // TODO finish or start again + public Player WhoPlaysNow(bool isFirstTurn) + { + if (players.Count == 0) + { + throw new Exception("you are exploring an empty collection\nthis should not have happened"); + } + + Player result = null; + + if (isFirstTurn) + { + result = players[0]; + } + else + { + result = players[NextIndex]; + } + + return result; + } + + /// + /// this feels very dirty + /// + /// + /// + /// + /// + public void PrepareNextPlayer(Player current) + { + if (players.Count == 0) + { + throw new Exception("you are exploring an empty collection\nthis should not have happened"); + } + if (current == null) + { + throw new ArgumentNullException(nameof(current), "param should not be null"); + } + if (!players.Contains(current)) + { + throw new ArgumentException("param could not be found in this collection\n did you forget to add it?", nameof(current)); + } + + //if (currentIndex >= players.Count() - 1) + if (players.Last() == current) + { + // if we've reached the last index, we need to loop back around + NextIndex = 0; + } + else + { + // else we can just move up one from current + NextIndex++; + } + } + /// /// update a player from to /// @@ -101,7 +175,7 @@ namespace Model.Players { throw new ArgumentNullException(nameof(toRemove), "param should not be null"); } - // the built-in HashSet.Remove() method will use our redefined Equals(), using Name only + // the built-in Remove() method will use our redefined Equals(), using Name only players.Remove(toRemove); } } From 92716d5f32f5daaedac1ac316a5bd9abdb658ced Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Tue, 20 Sep 2022 18:38:13 +0200 Subject: [PATCH 2/6] :necktie: Get started on the Game class --- Sources/Model/Game.cs | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Sources/Model/Game.cs diff --git a/Sources/Model/Game.cs b/Sources/Model/Game.cs new file mode 100644 index 0000000..2f0ca4c --- /dev/null +++ b/Sources/Model/Game.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + public class Game + { + public string Name { get; private set; } + + public bool IsFirstTurn { get; private set; } = false; + + private readonly List turns; + + ///
+ /// get a READ ONLY enumerable of all turns belonging to this game + /// + /// a readonly enumerable of all this game's turns + public IEnumerable GetHistory() => turns.AsEnumerable(); + + public PlayerManager PlayerManager { get; } + + public Game(string name, IEnumerable turns, PlayerManager playerManager) + { + Name = name; + this.turns = turns.ToList() ?? new List(); + PlayerManager = playerManager ?? new PlayerManager(); + } + + public Game(string name) + : this(name, null, null) + { } + + public void PerformTurn() + { + Player player = PlayerManager.WhoPlaysNow(IsFirstTurn); + if (IsFirstTurn) { IsFirstTurn = false; } // only true one time (on the first turn...) + + + // in a "faces" var, throw all the dice and stuff... + + Turn turn = Turn.CreateWithDefaultTime(new Player(player)/*, faces*/); //using a copy so that next line doesn't "change history" + PlayerManager.PrepareNextPlayer(player); + turns.Add(turn); + } + + + + + // TODO test and debug + public override string ToString() + { + StringBuilder sb = new(); + sb.AppendFormat("Game: {0}===========\n" + + "{1} are playing. {2} is next.\n" + + "Log:\n", + Name, + PlayerManager.GetAll().ToString(), + PlayerManager.WhoPlaysNow(IsFirstTurn)); + + foreach (Turn turn in this.turns) + { + sb.Append("\t" + turn.ToString()); + } + + return sb.ToString(); + } + } +} From 10a48032bd265e56b86b61906acb5170d2e87e53 Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Wed, 21 Sep 2022 17:05:21 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Move=20Game=20into?= =?UTF-8?q?=20Games?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/Model/Game.cs | 72 ------------------------------------- Sources/Model/Games/Game.cs | 58 +++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 76 deletions(-) delete mode 100644 Sources/Model/Game.cs diff --git a/Sources/Model/Game.cs b/Sources/Model/Game.cs deleted file mode 100644 index 2f0ca4c..0000000 --- a/Sources/Model/Game.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Model -{ - public class Game - { - public string Name { get; private set; } - - public bool IsFirstTurn { get; private set; } = false; - - private readonly List turns; - - /// - /// get a READ ONLY enumerable of all turns belonging to this game - /// - /// a readonly enumerable of all this game's turns - public IEnumerable GetHistory() => turns.AsEnumerable(); - - public PlayerManager PlayerManager { get; } - - public Game(string name, IEnumerable turns, PlayerManager playerManager) - { - Name = name; - this.turns = turns.ToList() ?? new List(); - PlayerManager = playerManager ?? new PlayerManager(); - } - - public Game(string name) - : this(name, null, null) - { } - - public void PerformTurn() - { - Player player = PlayerManager.WhoPlaysNow(IsFirstTurn); - if (IsFirstTurn) { IsFirstTurn = false; } // only true one time (on the first turn...) - - - // in a "faces" var, throw all the dice and stuff... - - Turn turn = Turn.CreateWithDefaultTime(new Player(player)/*, faces*/); //using a copy so that next line doesn't "change history" - PlayerManager.PrepareNextPlayer(player); - turns.Add(turn); - } - - - - - // TODO test and debug - public override string ToString() - { - StringBuilder sb = new(); - sb.AppendFormat("Game: {0}===========\n" + - "{1} are playing. {2} is next.\n" + - "Log:\n", - Name, - PlayerManager.GetAll().ToString(), - PlayerManager.WhoPlaysNow(IsFirstTurn)); - - foreach (Turn turn in this.turns) - { - sb.Append("\t" + turn.ToString()); - } - - return sb.ToString(); - } - } -} diff --git a/Sources/Model/Games/Game.cs b/Sources/Model/Games/Game.cs index cb339df..66d0233 100644 --- a/Sources/Model/Games/Game.cs +++ b/Sources/Model/Games/Game.cs @@ -1,8 +1,7 @@ -using System; +using Model.Players; using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Model.Games { @@ -10,11 +9,62 @@ namespace Model.Games { public string Name { get; private set; } - private readonly IEnumerable turns = new List(); + public bool IsFirstTurn { get; private set; } = false; - public Game(string name) + private readonly List turns; + + /// + /// get a READ ONLY enumerable of all turns belonging to this game + /// + /// a readonly enumerable of all this game's turns + public IEnumerable GetHistory() => turns.AsEnumerable(); + + public PlayerManager PlayerManager { get; } + + public Game(string name, IEnumerable turns, PlayerManager playerManager) { Name = name; + this.turns = turns.ToList() ?? new List(); + PlayerManager = playerManager ?? new PlayerManager(); + } + + public Game(string name) + : this(name, null, null) + { } + + public void PerformTurn() + { + Player player = PlayerManager.WhoPlaysNow(IsFirstTurn); + if (IsFirstTurn) { IsFirstTurn = false; } // only true one time (on the first turn...) + + + // in a "faces" var, throw all the dice and stuff... + + Turn turn = Turn.CreateWithDefaultTime(new Player(player)/*, faces*/); //using a copy so that next line doesn't "change history" + PlayerManager.PrepareNextPlayer(player); + turns.Add(turn); + } + + + + + // TODO test and debug + public override string ToString() + { + StringBuilder sb = new(); + sb.AppendFormat("Game: {0}===========\n" + + "{1} are playing. {2} is next.\n" + + "Log:\n", + Name, + PlayerManager.GetAll().ToString(), + PlayerManager.WhoPlaysNow(IsFirstTurn)); + + foreach (Turn turn in this.turns) + { + sb.Append("\t" + turn.ToString()); + } + + return sb.ToString(); } } } From d744b07e3b056c28a6253e1cb2e0d08465a04316 Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Wed, 21 Sep 2022 17:21:03 +0200 Subject: [PATCH 4/6] :recycle: Add FavGroup and refactor manager --- Sources/Model/Games/Game.cs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Sources/Model/Games/Game.cs b/Sources/Model/Games/Game.cs index 66d0233..0bcc2c8 100644 --- a/Sources/Model/Games/Game.cs +++ b/Sources/Model/Games/Game.cs @@ -1,4 +1,5 @@ -using Model.Players; +using Model.Dice; +using Model.Players; using System.Collections.Generic; using System.Linq; using System.Text; @@ -19,35 +20,35 @@ namespace Model.Games /// a readonly enumerable of all this game's turns public IEnumerable GetHistory() => turns.AsEnumerable(); - public PlayerManager PlayerManager { get; } + private readonly PlayerManager playerManager; - public Game(string name, IEnumerable turns, PlayerManager playerManager) + private readonly FavGroup favGroup; + + public Game(string name, IEnumerable turns, PlayerManager playerManager, FavGroup favGroup) { Name = name; this.turns = turns.ToList() ?? new List(); - PlayerManager = playerManager ?? new PlayerManager(); + this.playerManager = playerManager ?? new PlayerManager(); + this.favGroup = favGroup ?? new FavGroup(); } public Game(string name) - : this(name, null, null) + : this(name, null, null, null) { } public void PerformTurn() { - Player player = PlayerManager.WhoPlaysNow(IsFirstTurn); + Player player = playerManager.WhoPlaysNow(IsFirstTurn); if (IsFirstTurn) { IsFirstTurn = false; } // only true one time (on the first turn...) - // in a "faces" var, throw all the dice and stuff... + // throw favGroup's dice and record it in a "faces" var Turn turn = Turn.CreateWithDefaultTime(new Player(player)/*, faces*/); //using a copy so that next line doesn't "change history" - PlayerManager.PrepareNextPlayer(player); + playerManager.PrepareNextPlayer(player); turns.Add(turn); } - - - // TODO test and debug public override string ToString() { @@ -56,8 +57,8 @@ namespace Model.Games "{1} are playing. {2} is next.\n" + "Log:\n", Name, - PlayerManager.GetAll().ToString(), - PlayerManager.WhoPlaysNow(IsFirstTurn)); + playerManager.GetAll().ToString(), + playerManager.WhoPlaysNow(IsFirstTurn)); foreach (Turn turn in this.turns) { From 48db9ba6de11f86f1e157f04861199c05f336643 Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Wed, 21 Sep 2022 19:11:31 +0200 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=92=A1=E2=99=BB=EF=B8=8F=20Add=20to?= =?UTF-8?q?=20Game=20thanks=20to=20new=20classes,=20add=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/Data/Stub.cs | 7 +- Sources/Model/Dice/Die.cs | 16 ++- Sources/Model/Dice/FavGroup.cs | 7 +- Sources/Model/Games/Game.cs | 130 ++++++++++++++++++++++--- Sources/Model/Players/PlayerManager.cs | 6 +- 5 files changed, 138 insertions(+), 28 deletions(-) diff --git a/Sources/Data/Stub.cs b/Sources/Data/Stub.cs index a8ea95a..39d29d5 100644 --- a/Sources/Data/Stub.cs +++ b/Sources/Data/Stub.cs @@ -15,12 +15,7 @@ namespace Data public GameRunner LoadApp() { // this doesn't do much for now, because the classes aren't coded - List games = new() - { - new Game("a"), - new Game("b"), - new Game("c") - }; + List games = new(); PlayerManager gpm = new(); gpm.Add(new Player("Alice")); diff --git a/Sources/Model/Dice/Die.cs b/Sources/Model/Dice/Die.cs index 5ba8c68..7f2314a 100644 --- a/Sources/Model/Dice/Die.cs +++ b/Sources/Model/Dice/Die.cs @@ -1,10 +1,22 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Model.Dice.Faces; namespace Model.Dice { public class Die { - private IEnumerable faces; + private static readonly Random random = new Random(); + + private List faces; + + public AbstractDieFace Throw() + { + // next(x, y) --> [x, y[ + return faces[random.Next(0, faces.Count)]; + // replace with better algo later + } } + + } diff --git a/Sources/Model/Dice/FavGroup.cs b/Sources/Model/Dice/FavGroup.cs index 24bda81..b718e38 100644 --- a/Sources/Model/Dice/FavGroup.cs +++ b/Sources/Model/Dice/FavGroup.cs @@ -1,10 +1,13 @@ -using System.Collections.Generic; +using Model.Players; +using System.Collections.Generic; namespace Model.Dice { public class FavGroup { - private IEnumerable dice; + public IEnumerable Dice { get; private set; } + public string Name { get; private set; } + } } diff --git a/Sources/Model/Games/Game.cs b/Sources/Model/Games/Game.cs index 0bcc2c8..2ee0a41 100644 --- a/Sources/Model/Games/Game.cs +++ b/Sources/Model/Games/Game.cs @@ -1,17 +1,45 @@ using Model.Dice; +using Model.Dice.Faces; using Model.Players; +using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Text; namespace Model.Games { public class Game { - public string Name { get; private set; } + /// + /// the name of the game 😎 + /// + public string Name + { + get + { + return name; + } + set // GameRunner will need to take care of forbidding + // (or allowing) having two Games with the same name etc. + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("param should not be null or blank", nameof(value)); + } + name = value; + } + } + private string name; + /// + /// whether this game is new or not + /// public bool IsFirstTurn { get; private set; } = false; + /// + /// the turns that have been done so far + /// private readonly List turns; /// @@ -20,36 +48,112 @@ namespace Model.Games /// a readonly enumerable of all this game's turns public IEnumerable GetHistory() => turns.AsEnumerable(); + /// + /// the game's player manager, doing CRUD on players and switching whose turn it is + /// private readonly PlayerManager playerManager; + /// + /// the group of dice used for this game + /// private readonly FavGroup favGroup; - public Game(string name, IEnumerable turns, PlayerManager playerManager, FavGroup favGroup) + /// + /// constructs a Game with its own history of Turns. + /// If is null, starts a new history + /// + /// the name of the game 😎 + /// the turns that have been done so far + /// the game's player manager, doing CRUD on players and switching whose turn it is + /// the group of dice used for this game + public Game(string name, PlayerManager playerManager, FavGroup favGroup, IEnumerable turns) { Name = name; this.turns = turns.ToList() ?? new List(); - this.playerManager = playerManager ?? new PlayerManager(); - this.favGroup = favGroup ?? new FavGroup(); + this.playerManager = playerManager; + this.favGroup = favGroup; } - public Game(string name) - : this(name, null, null, null) + /// + /// constructs a Game with no history of turns. + /// + /// the name of the game 😎 + /// the game's player manager, doing CRUD on players and switching whose turn it is + /// the group of dice used for this game + public Game(string name, PlayerManager playerManager, FavGroup favGroup) + : this(name, playerManager, favGroup, null) { } - public void PerformTurn() + /// + /// performs a Turn, marks this Game as "started", and logs that Turn + /// + /// the player whose turn it is + public void PerformTurn(Player player) { - Player player = playerManager.WhoPlaysNow(IsFirstTurn); if (IsFirstTurn) { IsFirstTurn = false; } // only true one time (on the first turn...) + Turn turn = Turn.CreateWithDefaultTime( + new Player(player), + ThrowAll() //using a copy so that next line doesn't "change history" + ); + turns.Add(turn); + } + /// + /// finds whose turn it is + /// + /// the Player whose turn it is + public Player GetWhoPlaysNow() + { + return playerManager.WhoPlaysNow(IsFirstTurn); + } - // throw favGroup's dice and record it in a "faces" var + /// + /// throws all the Dice in FavGroup and returns a list of their Faces + /// + /// list of AbstractDieFaces after a throw + private List ThrowAll() + { + List faces = new(); + foreach (Die die in favGroup.Dice) + { + faces.Add(die.Throw()); + } + return faces; + } - Turn turn = Turn.CreateWithDefaultTime(new Player(player)/*, faces*/); //using a copy so that next line doesn't "change history" - playerManager.PrepareNextPlayer(player); - turns.Add(turn); + /// + /// asks the PlayerManager to prepare the next Player + /// + /// the Player whose turn it was + public void PrepareNextPlayer(Player currentPlayer) + { + playerManager.PrepareNextPlayer(currentPlayer); + } + + public Player AddPlayerToGame(Player player) + { + return playerManager.Add(player); + } + + public IEnumerable GetPlayersFromGame() + { + return playerManager.GetAll(); + } + + public Player UpdatePlayerInGame(Player oldPlayer, Player newPlayer) + { + return playerManager.Update(oldPlayer, newPlayer); } - // TODO test and debug + public void RemovePlayerFromGame(Player player) + { + playerManager.Remove(player); + } + + /// + /// represents a Game in string format + /// + /// a Game in string format public override string ToString() { StringBuilder sb = new(); diff --git a/Sources/Model/Players/PlayerManager.cs b/Sources/Model/Players/PlayerManager.cs index 273d6de..fbdda57 100644 --- a/Sources/Model/Players/PlayerManager.cs +++ b/Sources/Model/Players/PlayerManager.cs @@ -79,8 +79,6 @@ namespace Model.Players /// /// finds and returns the player whose turn it is - ///
- /// SHOULD HAVE NO SIDE EFFECT ///
/// /// @@ -93,8 +91,7 @@ namespace Model.Players throw new Exception("you are exploring an empty collection\nthis should not have happened"); } - Player result = null; - + Player result; if (isFirstTurn) { result = players[0]; @@ -103,7 +100,6 @@ namespace Model.Players { result = players[NextIndex]; } - return result; } From 15b7264fdec396889fe565a12df3bad5e4490e84 Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Wed, 21 Sep 2022 22:37:07 +0200 Subject: [PATCH 6/6] :rotating_light: Fix code smells --- Sources/Model/Players/PlayerManager.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/Model/Players/PlayerManager.cs b/Sources/Model/Players/PlayerManager.cs index fbdda57..f57159d 100644 --- a/Sources/Model/Players/PlayerManager.cs +++ b/Sources/Model/Players/PlayerManager.cs @@ -82,13 +82,11 @@ namespace Model.Players /// /// /// - - // TODO finish or start again public Player WhoPlaysNow(bool isFirstTurn) { if (players.Count == 0) { - throw new Exception("you are exploring an empty collection\nthis should not have happened"); + throw new MemberAccessException("you are exploring an empty collection\nthis should not have happened"); } Player result; @@ -114,7 +112,7 @@ namespace Model.Players { if (players.Count == 0) { - throw new Exception("you are exploring an empty collection\nthis should not have happened"); + throw new MemberAccessException("you are exploring an empty collection\nthis should not have happened"); } if (current == null) { @@ -125,7 +123,6 @@ namespace Model.Players throw new ArgumentException("param could not be found in this collection\n did you forget to add it?", nameof(current)); } - //if (currentIndex >= players.Count() - 1) if (players.Last() == current) { // if we've reached the last index, we need to loop back around