Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
tpetchel committed Mar 22, 2019
1 parent 0a0e4a1 commit 1697047
Show file tree
Hide file tree
Showing 76 changed files with 31,860 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.DS_Store

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
Expand Down
17 changes: 17 additions & 0 deletions TailSpin.SpaceGame.Web.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TailSpin.SpaceGame.Web", "TailSpin.SpaceGame.Web\TailSpin.SpaceGame.Web.csproj", "{A0C4E31E-AC75-4F39-9F59-0AA19D9B8F46}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A0C4E31E-AC75-4F39-9F59-0AA19D9B8F46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0C4E31E-AC75-4F39-9F59-0AA19D9B8F46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0C4E31E-AC75-4F39-9F59-0AA19D9B8F46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0C4E31E-AC75-4F39-9F59-0AA19D9B8F46}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
139 changes: 139 additions & 0 deletions Tailspin.SpaceGame.Web/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using TailSpin.SpaceGame.Web.Models;

namespace TailSpin.SpaceGame.Web.Controllers
{
public class HomeController : Controller
{
// High score repository.
private readonly IDocumentDBRepository<Score> _scoreRepository;
// User profile repository.
private readonly IDocumentDBRepository<Profile> _profileRespository;

public HomeController(
IDocumentDBRepository<Score> scoreRepository,
IDocumentDBRepository<Profile> profileRespository
)
{
_scoreRepository = scoreRepository;
_profileRespository = profileRespository;
}

public async Task<IActionResult> Index(
int page = 1,
int pageSize = 10,
string mode = "",
string region = ""
)
{
// Create the view model with initial values we already know.
var vm = new LeaderboardViewModel
{
Page = page,
PageSize = pageSize,
SelectedMode = mode,
SelectedRegion = region,

GameModes = new List<string>()
{
"Solo",
"Duo",
"Trio"
},

GameRegions = new List<string>()
{
"Milky Way",
"Andromeda",
"Pinwheel",
"NGC 1300",
"Messier 82",
}
};

try
{
// Form the query predicate.
// This expression selects all scores that match the provided game
// mode and region (map).
// Select the score if the game mode or region is empty.
Expression<Func<Score, bool>> queryPredicate = score =>
(string.IsNullOrEmpty(mode) || score.GameMode == mode) &&
(string.IsNullOrEmpty(region) || score.GameRegion == region);

// Fetch the total number of results in the background.
var countItemsTask = _scoreRepository.CountItemsAsync(queryPredicate);

// Fetch the scores that match the current filter.
IEnumerable<Score> scores = await _scoreRepository.GetItemsAsync(
queryPredicate, // the predicate defined above
score => score.HighScore, // sort descending by high score
page - 1, // subtract 1 to make the query 0-based
pageSize
);

// Wait for the total count.
vm.TotalResults = await countItemsTask;

// Set previous and next hyperlinks.
if (page > 1)
{
vm.PrevLink = $"/?page={page - 1}&pageSize={pageSize}&mode={mode}&region={region}#leaderboard";
}
if (vm.TotalResults > page * pageSize)
{
vm.NextLink = $"/?page={page + 1}&pageSize={pageSize}&mode={mode}&region={region}#leaderboard";
}

// Fetch the user profile for each score.
// This creates a list that's parallel with the scores collection.
var profiles = new List<Task<Profile>>();
foreach (var score in scores)
{
profiles.Add(_profileRespository.GetItemAsync(score.ProfileId));
}
Task<Profile>.WaitAll(profiles.ToArray());

// Combine each score with its profile.
vm.Scores = scores.Zip(profiles, (score, profile) => new ScoreProfile { Score = score, Profile = profile.Result });

return View(vm);
}
catch (Exception ex)
{
return View(vm);
}
}

[Route("/profile/{id}")]
public async Task<IActionResult> Profile(string id, string rank="")
{
try
{
// Fetch the user profile with the given identifier.
return View(new ProfileViewModel { Profile = await _profileRespository.GetItemAsync(id), Rank = rank });
}
catch (Exception ex)
{
return RedirectToAction("/");
}
}

public IActionResult Privacy()
{
return View();
}

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
50 changes: 50 additions & 0 deletions Tailspin.SpaceGame.Web/IDocumentDBRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
using TailSpin.SpaceGame.Web.Models;

namespace TailSpin.SpaceGame.Web
{
public interface IDocumentDBRepository<T> where T : Model
{
/// <summary>
/// Retrieves the item from the store with the given identifier.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the retrieved item.
/// </returns>
/// <param name="id">The identifier of the item to retrieve.</param>
Task<T> GetItemAsync(string id);

/// <summary>
/// Retrieves items from the store that match the given query predicate.
/// Results are given in descending order by the given ordering predicate.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the collection of retrieved items.
/// </returns>
/// <param name="queryPredicate">Predicate that specifies which items to select.</param>
/// <param name="orderDescendingPredicate">Predicate that specifies how to sort the results in descending order.</param>
/// <param name="page">The 1-based page of results to return.</param>
/// <param name="pageSize">The number of items on a page.</param>
Task<IEnumerable<T>> GetItemsAsync(
Expression<Func<T, bool>> queryPredicate,
Expression<Func<T, int>> orderDescendingPredicate,
int page = 1,
int pageSize = 10
);

/// <summary>
/// Retrieves the number of items that match the given query predicate.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the number of items that match the query predicate.
/// </returns>
/// <param name="queryPredicate">Predicate that specifies which items to select.</param>
Task<int> CountItemsAsync(Expression<Func<T, bool>> queryPredicate);
}
}
81 changes: 81 additions & 0 deletions Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Newtonsoft.Json;
using TailSpin.SpaceGame.Web.Models;

namespace TailSpin.SpaceGame.Web
{
public class LocalDocumentDBRepository<T> : IDocumentDBRepository<T> where T : Model
{
// An in-memory list of all items in the collection.
private readonly List<T> _items;

public LocalDocumentDBRepository(string fileName)
{
// Serialize the items from the provided JSON document.
_items = JsonConvert.DeserializeObject<List<T>>(File.ReadAllText(fileName));
}

/// <summary>
/// Retrieves the item from the store with the given identifier.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the retrieved item.
/// </returns>
/// <param name="id">The identifier of the item to retrieve.</param>
public Task<T> GetItemAsync(string id)
{
return Task<T>.FromResult(_items.Single(item => item.Id == id));
}

/// <summary>
/// Retrieves items from the store that match the given query predicate.
/// Results are given in descending order by the given ordering predicate.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the collection of retrieved items.
/// </returns>
/// <param name="queryPredicate">Predicate that specifies which items to select.</param>
/// <param name="orderDescendingPredicate">Predicate that specifies how to sort the results in descending order.</param>
/// <param name="page">The 1-based page of results to return.</param>
/// <param name="pageSize">The number of items on a page.</param>
public Task<IEnumerable<T>> GetItemsAsync(
Expression<Func<T, bool>> queryPredicate,
Expression<Func<T, int>> orderDescendingPredicate,
int page = 1, int pageSize = 10
)
{
var result = _items.AsQueryable()
.Where(queryPredicate) // filter
.OrderByDescending(orderDescendingPredicate) // sort
.Skip(page * pageSize) // find page
.Take(pageSize) // take items
.AsEnumerable(); // make enumeratable

return Task<IEnumerable<T>>.FromResult(result);
}

/// <summary>
/// Retrieves the number of items that match the given query predicate.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the number of items that match the query predicate.
/// </returns>
/// <param name="queryPredicate">Predicate that specifies which items to select.</param>
public Task<int> CountItemsAsync(Expression<Func<T, bool>> queryPredicate)
{
var count = _items.AsQueryable()
.Where(queryPredicate) // filter
.Count(); // count

return Task<int>.FromResult(count);
}
}
}
9 changes: 9 additions & 0 deletions Tailspin.SpaceGame.Web/Models/ErrorViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TailSpin.SpaceGame.Web.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
43 changes: 43 additions & 0 deletions Tailspin.SpaceGame.Web/Models/LeaderboardViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;

namespace TailSpin.SpaceGame.Web.Models
{
public class LeaderboardViewModel
{
// The game mode selected in the view.
public string SelectedMode { get; set; }
// The game region (map) selected in the view.
public string SelectedRegion { get; set; }
// The current page to be shown in the view.
public int Page { get; set; }
// The number of items to show per page in the view.
public int PageSize { get; set; }

// The scores to display in the view.
public IEnumerable<ScoreProfile> Scores { get; set; }
// The game modes to display in the view.
public IEnumerable<string> GameModes { get; set; }
// The game regions (maps) to display in the view.
public IEnumerable<string> GameRegions { get; set; }

// Hyperlink to the previous page of results.
// This is empty if this is the first page.
public string PrevLink { get; set; }
// Hyperlink to the next page of results.
// This is empty if this is the last page.
public string NextLink { get; set; }
// The total number of results for the selected game mode and region in the view.
public int TotalResults { get; set; }
}

/// <summary>
/// Combines a score and a user profile.
/// </summary>
public struct ScoreProfile
{
// The player's score.
public Score Score;
// The player's profile.
public Profile Profile;
}
}
14 changes: 14 additions & 0 deletions Tailspin.SpaceGame.Web/Models/Model.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Newtonsoft.Json;

namespace TailSpin.SpaceGame.Web.Models
{
/// <summary>
/// Base class for data models.
/// </summary>
public abstract class Model
{
// The value that uniquely identifies this object.
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
}
}
19 changes: 19 additions & 0 deletions Tailspin.SpaceGame.Web/Models/Profile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Newtonsoft.Json;

namespace TailSpin.SpaceGame.Web.Models
{
public class Profile : Model
{
// The player's user name.
[JsonProperty(PropertyName = "userName")]
public string UserName { get; set; }

// The URL of the player's avatar image.
[JsonProperty(PropertyName = "avatarUrl")]
public string AvatarUrl { get; set; }

// The achievements the player earned.
[JsonProperty(PropertyName = "achievements")]
public string[] Achievements { get; set; }
}
}
Loading

0 comments on commit 1697047

Please sign in to comment.