Skip to content

Commit

Permalink
[C#] Add demo-RollerSkill sample (microsoft#112)
Browse files Browse the repository at this point in the history
* Add C# version of demo-RollerSkill sample

* Update sample to BotBuilder v3.5.9

* [CSharp-RollerSkill] Update to BotBuilder v3.8.0
  • Loading branch information
ejadib authored and RobStand committed May 11, 2017
1 parent 6b3df2c commit f5179c3
Show file tree
Hide file tree
Showing 21 changed files with 1,302 additions and 0 deletions.
33 changes: 33 additions & 0 deletions CSharp/demo-RollerSkill/App_Start/WebApiConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace RollerSkillBot
{
using System.Web.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Json settings
config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
};

// Web API configuration and services

// Web API routes
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
}
}
27 changes: 27 additions & 0 deletions CSharp/demo-RollerSkill/Controllers/MessagesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace RollerSkillBot
{
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using RollerSkillBot.Dialogs;

[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
await Conversation.SendAsync(activity, () => new RootDispatchDialog());

var response = Request.CreateResponse(HttpStatusCode.OK);

return response;
}
}
}
70 changes: 70 additions & 0 deletions CSharp/demo-RollerSkill/Dialogs/CreateGameDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
namespace RollerSkillBot.Dialogs
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using RollerSkillBot.Properties;

[Serializable]
public class CreateGameDialog : IDialog<GameData>
{
public async Task StartAsync(IDialogContext context)
{
context.UserData.SetValue<GameData>(Utils.GameDataKey, new GameData());

var descriptions = new List<string>() { "4 Sides", "6 Sides", "8 Sides", "10 Sides", "12 Sides", "20 Sides" };
var choices = new Dictionary<string, IReadOnlyList<string>>()
{
{ "4", new List<string> { "four", "for", "4 sided", "4 sides" } },
{ "6", new List<string> { "six", "sex", "6 sided", "6 sides" } },
{ "8", new List<string> { "eight", "8 sided", "8 sides" } },
{ "10", new List<string> { "ten", "10 sided", "10 sides" } },
{ "12", new List<string> { "twelve", "12 sided", "12 sides" } },
{ "20", new List<string> { "twenty", "20 sided", "20 sides" } }
};

var promptOptions = new PromptOptionsWithSynonyms<string>(
Resources.ChooseSides,
choices: choices,
descriptions: descriptions,
speak: SSMLHelper.Speak(Utils.RandomPick(Resources.ChooseSidesSSML)));

PromptDialog.Choice(context, this.DiceChoiceReceivedAsync, promptOptions);
}

private async Task DiceChoiceReceivedAsync(IDialogContext context, IAwaitable<string> result)
{
GameData game;
if (context.UserData.TryGetValue<GameData>(Utils.GameDataKey, out game))
{
int sides;
if (int.TryParse(await result, out sides))
{
game.Sides = sides;
context.UserData.SetValue<GameData>(Utils.GameDataKey, game);
}

var promptText = string.Format(Resources.ChooseCount, sides);

// TODO: When supported, update to pass Min and Max paramters
var promptOption = new PromptOptions<long>(promptText, speak: SSMLHelper.Speak(Utils.RandomPick(Resources.ChooseCountSSML)));

var prompt = new PromptDialog.PromptInt64(promptOption, min: 1, max: 100);
context.Call<long>(prompt, this.DiceNumberReceivedAsync);
}
}

private async Task DiceNumberReceivedAsync(IDialogContext context, IAwaitable<long> result)
{
GameData game;
if (context.UserData.TryGetValue<GameData>(Utils.GameDataKey, out game))
{
game.Count = await result;
context.UserData.SetValue<GameData>(Utils.GameDataKey, game);
}

context.Done(game);
}
}
}
40 changes: 40 additions & 0 deletions CSharp/demo-RollerSkill/Dialogs/HelpDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace RollerSkillBot.Dialogs
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using RollerSkillBot.Properties;

[Serializable]
public class HelpDialog : IDialog<object>
{
private const string RollDiceOptionValue = "roll some dice";

private const string PlayCrapsOptionValue = "play craps";

public async Task StartAsync(IDialogContext context)
{
var message = context.MakeMessage();
message.Speak = SSMLHelper.Speak(Resources.HelpSSML);
message.InputHint = InputHints.AcceptingInput;

message.Attachments = new List<Attachment>
{
new HeroCard(Resources.HelpTitle)
{
Buttons = new List<CardAction>
{
new CardAction(ActionTypes.ImBack, "Roll Dice", value: RollDiceOptionValue),
new CardAction(ActionTypes.ImBack, "Play Craps", value: PlayCrapsOptionValue)
}
}.ToAttachment()
};

await context.PostAsync(message);

context.Done<object>(null);
}
}
}
147 changes: 147 additions & 0 deletions CSharp/demo-RollerSkill/Dialogs/PlayGameDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
namespace RollerSkillBot.Dialogs
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using RollerSkillBot.Properties;

[Serializable]
public class PlayGameDialog : IDialog<object>
{
private const string RollAgainOptionValue = "roll again";

private const string NewGameOptionValue = "new game";

private GameData gameData;

public PlayGameDialog(GameData gameData)
{
this.gameData = gameData;
}

public async Task StartAsync(IDialogContext context)
{
if (this.gameData == null)
{
if (!context.UserData.TryGetValue<GameData>(Utils.GameDataKey, out this.gameData))
{
// User started session with "roll again" so let's just send them to
// the 'CreateGameDialog'
context.Done<object>(null);
}
}

int total = 0;
var randomGenerator = new Random();
var rolls = new List<int>();

// Generate Rolls
for (int i = 0; i < this.gameData.Count; i++)
{
var roll = randomGenerator.Next(1, this.gameData.Sides);
total += roll;
rolls.Add(roll);
}

// Format rolls results
var result = string.Join(" . ", rolls.ToArray());
bool multiLine = rolls.Count > 5;

var card = new HeroCard()
{
Subtitle = string.Format(
this.gameData.Count > 1 ? Resources.CardSubtitlePlural : Resources.CardSubtitleSingular,
this.gameData.Count,
this.gameData.Sides),
Buttons = new List<CardAction>()
{
new CardAction(ActionTypes.ImBack, "Roll Again", value: RollAgainOptionValue),
new CardAction(ActionTypes.ImBack, "New Game", value: NewGameOptionValue)
}
};

if (multiLine)
{
card.Text = result;
}
else
{
card.Title = result;
}

var message = context.MakeMessage();
message.Attachments = new List<Attachment>()
{
card.ToAttachment()
};

// Determine bots reaction for speech purposes
string reaction = "normal";

var min = this.gameData.Count;
var max = this.gameData.Count * this.gameData.Sides;
var score = total / max;
if (score == 1)
{
reaction = "Best";
}
else if (score == 0)
{
reaction = "Worst";
}
else if (score <= 0.3)
{
reaction = "Bad";
}
else if (score >= 0.8)
{
reaction = "Good";
}

// Check for special craps rolls
if (this.gameData.Type == "Craps")
{
switch (total)
{
case 2:
case 3:
case 12:
reaction = "CrapsLose";
break;
case 7:
reaction = "CrapsSeven";
break;
case 11:
reaction = "CrapsEleven";
break;
default:
reaction = "CrapsRetry";
break;
}
}

// Build up spoken response
var spoken = string.Empty;
if (this.gameData.Turns == 0)
{
spoken += Utils.RandomPick(Resources.ResourceManager.GetString($"Start{this.gameData.Type}GameSSML"));
}

spoken += Utils.RandomPick(Resources.ResourceManager.GetString($"{reaction}RollReactionSSML"));

message.Speak = SSMLHelper.Speak(spoken);

// Increment number of turns and store game to roll again
this.gameData.Turns++;
context.UserData.SetValue<GameData>(Utils.GameDataKey, this.gameData);

// Send card and bots reaction to user.
message.InputHint = InputHints.AcceptingInput;
await context.PostAsync(message);

context.Done<object>(null);
}
}
}
Loading

0 comments on commit f5179c3

Please sign in to comment.