/// <summary> /// Uses an enumerable list of frame data to create a list of FrameViewModels /// </summary> /// <param name="framesData"></param> /// <returns></returns> public static IEnumerable <FrameViewModel> CreateFrameViewModels(IEnumerable <FrameDataModel> framesData) { // Prevent duplicate enumeration. List <FrameDataModel> frameDataModels = framesData.ToList(); List <FrameViewModel> output = new List <FrameViewModel>(); for (Int32 i = 0; i < frameDataModels.Count; i++) { FrameDataModel thisFrame = frameDataModels.ElementAt(i); FrameDataModel nextFrame = frameDataModels.ElementAtOrDefault(i + 1); FrameDataModel nextNextFrame = frameDataModels.ElementAtOrDefault(i + 2); FrameViewModel newView = new FrameViewModel { Throws = GetThrowCharacters(thisFrame), FrameNumber = frameDataModels.IndexOf(thisFrame) + 1 }; if (CanScore(thisFrame, nextFrame, nextNextFrame)) { Int32 score = (thisFrame.FirstThrow ?? 0) + (thisFrame.SecondThrow ?? 0) + (thisFrame.ThirdThrow ?? 0); // If this is a spare, add the first throw of next frame. // Bonus points only count for non-final frames. if (thisFrame.FrameNumber < 10 && thisFrame.IsSpare) { score += nextFrame?.FirstThrow ?? 0; } // If this is a strike... if (thisFrame.FrameNumber < 10 && thisFrame.IsStrike) { // For frame 1-8, next frame being strikes means add 10 to next-next's first throw. if (thisFrame.FrameNumber < 9 && nextFrame != null && nextFrame.IsStrike) { score += 10 + (nextNextFrame?.FirstThrow ?? 0); } // Otherwise, use next's first and second throw. else { score += (nextFrame?.FirstThrow ?? 0) + (nextFrame?.SecondThrow ?? 0); } } newView.Score = score; } output.Add(newView); } return(output); }
/// <summary> /// Creates Frame Data List from FrameDataModel class. /// </summary> /// <returns>String list of Frame data model constants</returns> public List <string> FrameDataList() { FrameDataModel fdm = new FrameDataModel(); List <string> fdmList = new List <string> { fdm.LargeAdvantageConst, fdm.SlightAdvantageConst, fdm.NeutralEvenConst, fdm.SlightDisadvantageConst, fdm.LargeDisadvantageConst }; return(fdmList); }
/// <summary> /// Gets the list of throw characters for this specific Frame. /// </summary> /// <param name="f">The frame to generate characters for.</param> /// <returns></returns> private static List <Char> GetThrowCharacters(FrameDataModel f) { List <char> throwCharacters = new List <Char>(); if (f.FirstThrow.HasValue) { throwCharacters.Add(f.FirstThrow == 10 ? 'X' : f.FirstThrow.ToString()[0]); } if (f.SecondThrow.HasValue) { if (f.SecondThrow == 10) { throwCharacters.Add('X'); } else if (f.FirstThrow + f.SecondThrow == 10) { throwCharacters.Add('/'); } else { throwCharacters.Add(f.SecondThrow.ToString()[0]); } ; } if (f.ThirdThrow.HasValue) { if (f.ThirdThrow == 10) { throwCharacters.Add('X'); } // If we weren't already a Spare, then mark Spare. else if (f.FirstThrow + f.SecondThrow != 10 && f.ThirdThrow + f.SecondThrow == 10) { throwCharacters.Add('/'); } else { throwCharacters.Add(f.SecondThrow.ToString()[0]); } ; } return(throwCharacters); }
public async Task <IActionResult> CreateGame([FromBody] CreateGameRequest request) { if (!ModelState.IsValid) { return(BadRequest(ModelState)); } PlayerDataModel player = await _context.Players.FindAsync(request.PlayerId); if (player == null) { return(BadRequest($"No player with Id {request.PlayerId} exists.")); } // Create new Game entry in DB. GameDataModel gameDataModel = new GameDataModel { Player = player, CreationDate = DateTime.Now, Frames = new List <FrameDataModel>() }; _context.Games.Add(gameDataModel); await _context.SaveChangesAsync(); // Create first Frame entry for the new Game. FrameDataModel firstFrameDataModel = new FrameDataModel { CreationDate = DateTime.Now, GameId = gameDataModel.Id, FrameNumber = 1 }; _context.Frames.Add(firstFrameDataModel); await _context.SaveChangesAsync(); // Update new Game entry with the new Frame and throw. gameDataModel.Frames.Add(firstFrameDataModel); gameDataModel.CurrentFrameId = gameDataModel.Frames.First().Id; gameDataModel.CurrentThrow = 1; await _context.SaveChangesAsync(); return(CreatedAtAction("GetGame", new { gameId = gameDataModel.Id }, ViewModelFactory.CreateGameViewModel(gameDataModel))); }
/// <summary> /// Determine whether or not this Frame (based on the next two frames) can be scored. /// </summary> /// <param name="thisFrame"></param> /// <param name="nextFrame"></param> /// <param name="nextNextFrame"></param> /// <returns></returns> private static bool CanScore(FrameDataModel thisFrame, FrameDataModel nextFrame, FrameDataModel nextNextFrame) { // If we are looking at a spare, we need the next Frame to have a value in order to score. if (thisFrame.IsSpare) { // Frame 10 will only depend on this frame's third throw. if (thisFrame.FrameNumber == 10) { return(thisFrame?.ThirdThrow != null); } // Otherwise, we check for next frame's first throw. return(nextFrame?.FirstThrow != null); } // If we are looking at a strike... if (thisFrame.IsStrike) { // Frame 10 will only ever be dependent on this frame. if (thisFrame.FrameNumber == 10) { return(thisFrame?.SecondThrow != null && thisFrame?.ThirdThrow != null); } // Frame 9 will only ever be dependent on next frame. if (thisFrame.FrameNumber == 9) { return(nextFrame?.FirstThrow != null && nextFrame.SecondThrow != null); } // If next frame is a strike, check if next-next has a value. if (nextFrame != null && nextFrame.IsStrike) { return(nextNextFrame?.FirstThrow != null); } // Otherwise check that nextFrame has a Second Throw. return(nextFrame?.SecondThrow != null); } // Otherwise, we can score when the frame is finished. return(thisFrame.IsFinished); }
public async Task <IActionResult> AddThrow([FromRoute] Guid gameId, [FromBody] CreateThrowRequest request) { if (!ModelState.IsValid) { return(BadRequest(ModelState)); } if (request.PinsDropped > 10) { return(BadRequest("A maximum of 10 pins may be dropped.")); } GameDataModel thisGame = await _context.Games .Include(g => g.Player) .Include(g => g.Frames) .SingleOrDefaultAsync(g => g.Id == gameId); if (thisGame == null) { return(BadRequest($"No game with Id {gameId} exists.")); } if (thisGame.IsFinished) { return(BadRequest("This game is complete, no more throws allowed.")); } // Set current throw to the value we just received. FrameDataModel currentFrame = await _context.Frames.SingleAsync(f => f.Id == thisGame.CurrentFrameId); // First Throw if (thisGame.CurrentThrow == 1) { currentFrame.FirstThrow = request.PinsDropped; if (request.PinsDropped == 10) { currentFrame.IsStrike = true; currentFrame.IsFinished = true; } } // Second Throw else if (thisGame.CurrentThrow == 2) { currentFrame.SecondThrow = request.PinsDropped; // Mark if Spare. currentFrame.IsSpare = currentFrame.FirstThrow + request.PinsDropped == 10; // Non-Final Frames are finished at this point, and can score if not a strike or spare. if (currentFrame.FrameNumber < 10) { currentFrame.IsFinished = true; } // Final Frame. else { currentFrame.IsFinished = !(currentFrame.IsStrike || currentFrame.IsSpare); thisGame.IsFinished = !(currentFrame.IsStrike || currentFrame.IsSpare); } } // Third Throw (Final Frame Only) else if (thisGame.CurrentThrow == 3) { currentFrame.ThirdThrow = request.PinsDropped; currentFrame.IsFinished = true; thisGame.IsFinished = true; } // If we need a new frame, create one and set it to throw 1 of that new frame. Boolean needsNewFrame = currentFrame.IsFinished && currentFrame.FrameNumber < 10; if (needsNewFrame) { FrameDataModel newFrame = new FrameDataModel { CreationDate = DateTime.Now, GameId = thisGame.Id, FrameNumber = currentFrame.FrameNumber + 1 }; _context.Frames.Add(newFrame); await _context.SaveChangesAsync(); thisGame.Frames.Add(newFrame); thisGame.CurrentFrameId = newFrame.Id; thisGame.CurrentThrow = 1; } else if (!thisGame.IsFinished) { thisGame.CurrentThrow++; } await _context.SaveChangesAsync(); return(Ok(ViewModelFactory.CreateGameViewModel(thisGame))); }
/// <summary> /// Generates in-memory mock data for FrameDataModels. /// </summary> /// <returns></returns> private List <FrameDataModel> MockFrameData(Guid gameId) { FrameDataModel Frame1 = new FrameDataModel { Id = Guid.NewGuid(), CreationDate = DateTime.UnixEpoch, GameId = gameId, FrameNumber = 1, FirstThrow = 5, SecondThrow = 5, IsSpare = true, IsFinished = true }; FrameDataModel Frame2 = new FrameDataModel { Id = Guid.NewGuid(), CreationDate = DateTime.UnixEpoch.AddMinutes(1), GameId = gameId, FrameNumber = 2, FirstThrow = 10, IsStrike = true, IsFinished = true }; FrameDataModel Frame3 = new FrameDataModel { Id = Guid.NewGuid(), CreationDate = DateTime.UnixEpoch.AddMinutes(2), GameId = gameId, FrameNumber = 3, FirstThrow = 1, SecondThrow = 8, IsFinished = true }; FrameDataModel Frame4 = new FrameDataModel { Id = Guid.NewGuid(), CreationDate = DateTime.UnixEpoch.AddMinutes(1), GameId = gameId, FrameNumber = 4, FirstThrow = 10, IsStrike = true, IsFinished = true }; FrameDataModel Frame5 = new FrameDataModel { Id = Guid.NewGuid(), CreationDate = DateTime.UnixEpoch.AddMinutes(1), GameId = gameId, FrameNumber = 5, FirstThrow = 10, IsStrike = true, IsFinished = true }; FrameDataModel Frame6 = new FrameDataModel { Id = Guid.NewGuid(), CreationDate = DateTime.UnixEpoch.AddMinutes(2), GameId = gameId, FrameNumber = 6, FirstThrow = 6 }; return(new List <FrameDataModel> { Frame1, Frame2, Frame3, Frame4, Frame5, Frame6 }); }