/// <summary> /// Save hub message and sends it to admin /// </summary> /// <param name="gridId">GridId</param> /// <param name="accessCode">Access Code</param> /// <param name="message">The message</param> /// <returns></returns> private async Task HubMessageAsync(int gridId, string accessCode, string message, string method, long runtime, int verbosity) { using (var db = new PersistedRepository()) { // Add to message log var messageLog = new MessageLog(gridId, accessCode, message, method, runtime); db.Add(messageLog); db.Save(); } if(_segregLoop.Engine.Verbosity >= verbosity) { // Send to admin await AdminMessageAsync(message); } }
/// <summary> /// Relocation and type change choices of bots /// </summary> private async Task UpdateBots() { using (var db = new PersistedRepository()) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; // Loop over all running grids foreach (var g in db.Grids.Where(x => x.GridState == 5 & x.BotCount > 0)) { // Relocation decisions // Test if all real players are ready AND bots are not ready AND endround is not yet reached if (!g.Players.Any(p => !p.IsBot & !p.IsReady) & !g.Players.Any(p => p.IsBot & p.IsReady) & g.Round < g.RoundCount & g.GameTypeId == 1) { var movedBots = 0; // Let bots decide if they want to move foreach (var p in g.Players.Where(x => x.IsBot)) { // Try to move the bot db.Game.BotRelocationDecision(g, p); if (p.RequestMove) movedBots++; } // Save changes db.Save(); // Report back await GridMessageAsync(g, "bots moved (" + movedBots.ToString() + ")", "UpdateBots", stopwatch.ElapsedMilliseconds, 2); } // Type change decisions // Test if all real players are ready AND bots are not ready AND endround is not yet reached if (!g.Players.Any(p => !p.IsBot & !p.IsReady) & !g.Players.Any(p => p.IsBot & p.IsReady) & g.Round < g.RoundCount & g.GameTypeId == 2) { var changedBots = 0; // Let bots decide if they want to move foreach (var p in g.Players.Where(x => x.IsBot)) { // Try to move the bot db.Game.BotTypeChangeDecision(g, p); if (p.RequestTypeChange) changedBots++; } // Save changes db.Save(); // Report back await GridMessageAsync(g, "bots changed (" + changedBots.ToString() + ")", "UpdateBots", stopwatch.ElapsedMilliseconds, 2); } } } }
/// <summary> /// Start and stop the game loop /// </summary> /// <param name="start">If true game loop starts and stops otherwise</param> public void ToggleLoops(bool start) { using (var db = new PersistedRepository()) { var engine = db.Engines.FirstOrDefault(e => e.EngineId == _segregLoop.Engine.EngineId); if (start) { engine.GameLoop = true; engine.EngineLoop = true; //engine.SweepLoop = true; db.Save(); _segregLoop.StartGameLoop(); _segregLoop.StartEngineLoop(); //_segregLoop.StartSweepLoop(); } else { engine.GameLoop = false; engine.EngineLoop = false; //engine.SweepLoop = false; db.Save(); _segregLoop.StopGameLoop(); _segregLoop.StopEngineLoop(); // _segregLoop.StopSweepLoop(); } } }
/// <summary> /// Adds message to log and broadcasts to admin /// </summary> /// <param name="g">The calling grid.</param> /// <param name="message">The message to save to the log file.</param> /// <param name="method">The method that called the grid message.</param> /// <param name="runtime">Runtime of the calling method.</param> /// <param name="verbosity">Verbosity level</param> private void GridMessage(Grid g, string message, string method, long runtime, int verbosity) { // Add to message log using (var db = new PersistedRepository()) { var messageLog = new MessageLog(g, message, method, runtime); db.Add(messageLog); db.Save(); } if (_engine.Verbosity >= verbosity) { // Send to admin string adminMessage = string.Format("Grid {0} : {1} : {2} : {3} [{4}]", g.GridId, g.GridState, g.Round, message, runtime); if (verbosity < 2) AdminMessage(adminMessage); } }
/// <summary> /// Push initial grid state to clients /// </summary> /// <returns></returns> private async Task PushInit() { using (var db = new PersistedRepository()) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; // Loop over all grids that are setup and ready foreach (var g in db.Grids.Where(x => x.GridState == 4)) { // Initial round 0, just send neighborhood if (!g.Players.Any(p => !p.IsReady)) { // Get neighborhoods of all players var nei = db.Game.Neighborhoods(g.GridId); // Loop over a shuffled list of players foreach (var p in g.Players.Shuffle(db.Random)) { // Get player neighborhood var pNei = nei.FirstOrDefault(n => n.PlayerId == p.PlayerId); if (pNei != null) { // Set player neighborhood p.Neighborhood = pNei; } } // Save Neighborhoods to database db.Save(); // Grid statistics db.Grid.GridStats(g, nei); // Logging await LogGrids(g.GridId); // Loop over a shuffled list of players foreach (var p in g.Players.Where(p => !p.IsBot).Shuffle(db.Random)) { // Compute client view state var cv = db.View.GetClientStatus(g, p); // Push state to clients Clients.Client(p.ConnectionId).nextRound(cv); } // Reset player flags db.Game.ResetPlayerFlags(g.GridId); // Initial Push completed db.Grid.SetState(g.GridId, 5); // Report back to admin await GridMessageAsync(g, "init push", "PushInit", stopwatch.ElapsedMilliseconds, 1); } } } }
/// <summary> /// Make copies of Grid, Cells, Players /// </summary> /// <param name="gridId">GridId</param> private async Task LogGrids(int gridId) { using (var db = new PersistedRepository()) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; // Make and save copy of the grid var grid = db.Grids.FirstOrDefault(g => g.GridId == gridId); db.Log.LogGrid(gridId); db.Log.BulkInsertCellLogs(gridId); db.Log.BulkInsertPlayerLogs(gridId); // Save back db.Save(); // Report back await GridMessageAsync(grid, "logged data", "LogGrids", stopwatch.ElapsedMilliseconds, 2); } }
/// <summary> /// Close a grid an move players /// </summary> private async Task CloseGrids() { using (var db = new PersistedRepository()) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; // Loop over all running grids foreach (var g in db.Grids.Where(x => x.GridState == 7)) { // Test if all players in grid are ready if (!g.Players.Any(p => !p.IsBot & !p.IsReady)) { // Reset player flags db.Game.ResetPlayerFlags(g.GridId); // Get all real players in the grid var players = g.Players.Where(p => !p.IsBot).ToList(); // Transfer Payoffs foreach (var p in players) { db.Player.TransferPayoff(g, p); } // Save back db.Save(); // Loop over all real players foreach (var p in players) { // Reset player round db.Player.GridPlayerReset(p.PlayerId); // Take the player off the cell/grid db.Cell.RemovePlayer(p.PlayerId); // Move Players to waiting, next grid, or exit poll db.Grid.MovePlayer(p.PlayerId, g.ExitGrid, _engine); // Setup url var url = db.Grid.GetUrl(_engine, p.GridId) + "/" + p.AccessCode; if (_engine.Testing) url += "?test"; // Navigate and push state to clients Clients.Client(p.ConnectionId).navigate(url); } // Make a last push of the gridview await PushGridViewAsync(); // Close grid db.Grid.SetState(g.GridId, 8); // Check if this is a chained grid // If yes, start the next grid in the chain. if (g.Chained > 0) db.Grid.StartNextInChain(g); // Take a last random number to check if seed of random number generator worked var rnd = _rng.Next(); // Report back await GridMessageAsync(g, "closed grid (" + rnd.ToString() +")", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); } } } }
/// <summary> /// Close a grid an move players /// </summary> public void CloseGrids() { using (var db = new PersistedRepository(Connect)) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; // Loop over all running grids foreach (var g in db.Grids.Where(x => x.GridState == 7)) { GridMessageAsync(g, "-- close 1", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Test if all players in grid are ready if (!g.Players.Any(p => !p.IsBot & !p.IsReady)) { // Reset player flags db.Game.ResetPlayerFlags(g.GridId); GridMessageAsync(g, "-- close 2", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Get all real players in the grid var players = g.Players.Where(p => !p.IsBot).ToList(); GridMessageAsync(g, "-- close 3", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Transfer Payoffs foreach (var p in players) { db.Player.TransferPayoff(g, p); GridMessageAsync(g, "-- -- transfered payoff", "CloseGrids", stopwatch.ElapsedMilliseconds, 2); } GridMessageAsync(g, "-- close 4", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Save back db.Save(); GridMessageAsync(g, "-- close 5", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Loop over all real players foreach (var p in players) { // Take the player off the cell/grid db.Cell.RemovePlayer(p.PlayerId); GridMessageAsync(g, "-- -- removed player", "CloseGrids", stopwatch.ElapsedMilliseconds, 2); // Move Players to waiting, next grid, or exit poll db.Grid.MovePlayer(p.PlayerId, g.ExitGrid, null); GridMessageAsync(g, "-- -- removed player", "CloseGrids", stopwatch.ElapsedMilliseconds, 2); // Navigate and push state to clients // Clients.Client(p.ConnectionId).navigate(db.Grid.GetUrl(g.ExitGrid) + "?a=" + p.AccessCode); } GridMessageAsync(g, "-- close 6", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Close grid db.Grid.SetState(g.GridId, 8); GridMessageAsync(g, "-- close 7", "CloseGrids", stopwatch.ElapsedMilliseconds, 1); // Take a last random number to check if seed of random number generator worked var rnd = _rng.Next(); // Report back GridMessageAsync(g, "closed grid (" + rnd.ToString() + ")", "CloseGrids", stopwatch.ElapsedMilliseconds, 0); } } } }
/// <summary> /// Push grid updates /// </summary> private async Task UpdateGrids() { using (var db = new PersistedRepository()) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; // Loop over all running grids foreach (var g in db.Grids.Where(x => x.GridState == 5)) { // Test if all players in grid are ready, then update and push if (!g.Players.Any(p => !p.IsReady) & g.Round < g.RoundCount) { g.Round = g.Round + 1; // Loop over a shuffled list of all players including bots and make all moves foreach (var p in g.Players.Shuffle(db.Random)) { // Make all moves in this round if (p.RequestMove) db.Game.RelocatePlayer(g.GridId, p.PlayerId); // Make all type changes in this round if (p.RequestTypeChange) { db.Game.ChangePlayerType(p.PlayerId); } } // Save changes db.Save(); // Get new neighborhoods of all players var nei = db.Game.Neighborhoods(g.GridId); // Loop over a shuffled list of all players including bots foreach (var p in g.Players.Shuffle(db.Random)) { // Compute results from last round var pNei = nei.FirstOrDefault(n => n.PlayerId == p.PlayerId); if (pNei != null) { // Set neighborhood p.Neighborhood = pNei; // Set points / payoff if (!p.HasTimeout) { p.LastPoints = db.Game.GetPayoff(g, pNei); p.GridPoints += p.LastPoints; p.TotalPoints += p.LastPoints; } // Set round p.Round = g.Round; } } // Save changes back to database db.Save(); // Grid statistics db.Grid.GridStats(g, nei); // Logging await LogGrids(g.GridId); //Push state foreach (var p in g.Players.Where(p => !p.IsBot).Shuffle(db.Random)) { // Compute client state var cv = db.View.GetClientStatus(g, p); // Push state to clients Clients.Client(p.ConnectionId).nextRound(cv); } // Reset player flags db.Game.ResetPlayerFlags(g.GridId); // Keep state on 5, set LastUpdateTime db.Grid.SetState(g.GridId, 5); // Report back to admin await GridMessageAsync(g, "updated grid (" + g.GridStats.MoveCount.ToString() + "/" + g.GridStats.TypeChangeCount.ToString() + ")", "UpdateGrids", stopwatch.ElapsedMilliseconds, 1); } // End of game is reached if (g.Round == g.RoundCount) { // Reset player flags db.Game.ResetPlayerFlags(g.GridId); //Push summary foreach (var p in g.Players.Where(p => !p.IsBot).Shuffle(db.Random)) { // Compute client state var cv = db.View.GetGridSummaryView(g, p); // Push state to clients Clients.Client(p.ConnectionId).summary(cv); } // Mark grid as in summary round db.Grid.SetState(g.GridId, 6); await GridMessageAsync(g, "grid summary", "UpdateGrids", stopwatch.ElapsedMilliseconds, 1); } // End of game summary is reached if (!g.Players.Any(p => !p.IsBot & !p.IsReady) & g.GridState == 6) { // Mark grid as completed as soon as we ran through all the rounds. db.Grid.SetState(g.GridId, 7); await GridMessageAsync(g, "grid completed", "UpdateGrids", stopwatch.ElapsedMilliseconds, 1); } } } }
/// <summary> /// Make copies of Grid, Cells, Players /// </summary> /// <param name="gridId">GridId</param> public void LogGrids(int gridId) { using (var db = new PersistedRepository(Connect)) { //Diagnostics var stopwatch = new Stopwatch(); stopwatch.Start(); // Set common random number generator db.Random = _rng; var g = db.Grids.FirstOrDefault(x => x.GridId == gridId); if(LogGridTable) db.Log.LogGrid(gridId); GridMessageAsync(g, "-- log grid", "LogGrids", stopwatch.ElapsedMilliseconds, 1); if (LogCellTable) db.Log.BulkInsertCellLogs(gridId); GridMessageAsync(g, "-- log cells", "LogGrids", stopwatch.ElapsedMilliseconds, 1); if (LogPlayerTable) db.Log.BulkInsertPlayerLogs(gridId); GridMessageAsync(g, "-- log players", "LogGrids", stopwatch.ElapsedMilliseconds, 1); // Save changes db.Save(); GridMessageAsync(g, "-- log saved", "LogGrids", stopwatch.ElapsedMilliseconds, 1); // Report back GridMessageAsync(g, "logged data", "LogGrids", stopwatch.ElapsedMilliseconds, 0); } }
/// <summary> /// Send Message to Console and Log /// </summary> /// <param name="g"></param> /// <param name="message"></param> /// <param name="method"></param> /// <param name="runtime"></param> public void GridMessageAsync(Grid g, string message, string method, long runtime, int debugLevel) { if (debugLevel <= DebugLevel) { if (DebugMethod.Length > 0) { if (DebugMethod == method) { // Send to admin string adminMessage = string.Format("Grid {0} : {1} : {2} : {3} [{4}]", g.GridId, g.GridState, g.Round, message, runtime); Console.WriteLine(adminMessage); } } else { // Send to admin string adminMessage = string.Format("Grid {0} : {1} : {2} : {3} [{4}]", g.GridId, g.GridState, g.Round, message, runtime); Console.WriteLine(adminMessage); } if (Logging) { using (var db = new PersistedRepository(Connect)) { // Add to message log var messageLog = new MessageLog(g, message, method, runtime); db.Add(messageLog); db.Save(); } } } }
/// <summary> /// Add entry to questions /// </summary> private void AddResponse(string accessCode) { using (var db = new PersistedRepository()) { var question = db.Questionnaires.FirstOrDefault(q => q.AccessCode.Equals(LblAccessCode.Text)); if (question == null) { db.Add(new Questionnaire { AccessCode = accessCode }); db.Save(); } } }
/// <summary> /// Save answers to questions /// </summary> /// <param name="step"></param> private void UpdateResponse(int step) { using (var db = new PersistedRepository()) { var question = db.Questionnaires.FirstOrDefault(q => q.AccessCode.Equals(LblAccessCode.Text)); if (question != null) { switch (step) { case 1: break; case 2: // Save demographic questions II question.Satisfaction = Satisfaction.SelectedIndex + 1; question.State = State.SelectedValue; question.Region = Region.SelectedIndex + 1; db.Save(); break; case 3: question.Yearborn = Age.Text; question.Gender = Gender.SelectedIndex + 1; question.Education = Education.SelectedIndex + 1; db.Save(); break; case 4: question.Employment = Employment.SelectedIndex + 1; db.Save(); break; case 5: question.Income = Income.Text; question.HitCount = HitCount.Text; db.Save(); break; case 6: question.Comments = Comments.Text; db.Save(); db.Code.InvalidateCode(LblAccessCode.Text); PerformCheckout(); break; } } } }
/// <summary> /// Generate new code for the debug / testing / auto mode /// </summary> /// <returns></returns> public string AutoAccessCode() { // Genereate a new access code var ac = System.Guid.NewGuid().ToString().Replace("-", ""); // Exit code must be invalid on auto code var ec = "invalid"; // Add the code to the database using (var db = new PersistedRepository()) { var code = new Code { AccessCode = ac, ExitCode = ec }; db.Add(code); db.Save(); } // Return the access code return ac; }