I want to create a lottery skill that takes 6 numbers from the user.
I'm currently learning by going through the samples and developer guides, and I can go through the guides and get a working skill that will take one input and then end the session. But I believe I need to create a dialog somehow, which is where I get stuck.
Design-wise, I'd like the dialog to go like this:
Alexa: Please provide the first number
User: 1
Alexa: and now the second...
User: 2
etc etc
But I think it would be OK if it went like this:
Alexa: Please call out 6 numbers
User: 1, 2, 3, 4, 5, 6.
Is this even possible? Will I have to create a custom slot type called "Numbers" and then put in the numbers, eg 1-50 or whatever the limit is?
At best, I can currently get it to ask for one number, so its really the dialog interaction that I'm stuck on. Has anyone ever even done anything like this?
Thanks.
Yes to both questions. You could string together a response with 6 different custom slots. "User: My numbers are {num1}, {num2}, {num3}, {num4}, {num5}, {num6} " and make them all required using the skills beta developer. However, it will be a rather bad user experience if the user does not phrase their answer appropriately and Alexa has to ask follow up questions to obtain each number. The last problem you'll run into is that while a custom slot could be defined to contain the numbers 1-50 alexa will generally recognize similar values to those provided in a custom slot, such as numbers from 50-99. It would then be up to you to check that the values you receive are between 1 and 50. If not you'd want to ask the user to provide a different number in the appropriate range.
Conclusion: You'll want to have individual interactions where a user provides a single number at a time.
Alexa:"you will be prompted for 6 numbers between 1 and 50 please state them one at a time. Choose your first number."
User:"50"
Alexa:"Your First number is 50, Next number."...
You can implement this using a single intent. let's name that intent GetNumberIntent. GetNumberIntent will have sample uterances along the line of
{number}
pick {number}
choose {number}
where {number} is a custom slot type or simply AMAZON.NUMBER. It will then be up to you to check that the number is between 1 and 50.
I program in Node.js using the SDK. Your implementation may vary depending upon your language choice.
What I would do is define 6 different state handlers. Each handler should have the GetNumberIntent. When a GetNumberIntent is returned if the slot value is apropriate store the value to the session data and or dynamodb and move forward to the next state. If the slot value is invalid stay for example at state "NumberInputFiveStateHandlers" until a good value is received then change state to the next "NumberInputSixStateHandlers"
var NumberInputFiveStateHandlers = Alexa.CreateStateHandler(states.NUMFIVEMODE, {
'NewSession': function () {
this.emit('NewSession'); // Uses the handler in newSessionHandlers
},
//Primary Intents
'GetNumberIntent': function () {
let message = ` `;
let reprompt = ` `;
let slotValue = this.event.request.intent.slots.number.value;
if(parseInt(slotValue) >= 1 && parseInt(slotValue) <= 50){
this.handler.state = states.NUMSIXMODE;
this.attributes['NUMBERFIVE'] = this.event.request.intent.slots.number.value;
message = ` Your fifth number is `+slotValue+`. please select your sixth value. `;
reprompt = ` please select your sixth value. `;
}else{
message = ` The number `+slotValue)+` is not in the desired range between 1 and 50. please select a valid fifth number. `;
reprompt = ` please select your fifth value. `;
}
this.emit(':ask',message,reprompt);
},
//Help Intents
"InformationIntent": function() {
console.log("INFORMATION");
var message = ` You've been asked to choose a lottery number between 1 and 50. Please say your selection.`;
this.emit(':ask', message, message);
},
"AMAZON.StopIntent": function() {
console.log("STOPINTENT");
this.emit(':tell', "Goodbye!");
},
"AMAZON.CancelIntent": function() {
console.log("CANCELINTENT");
this.emit(':tell', "Goodbye!");
},
'AMAZON.HelpIntent': function() {
var message = `You're playing lottery. you'll be picking six numbers to play the game. For help with your current situation say Information. otherwise you may exit the game by saying quit.`;
this.emit(':ask', message, message);
},
//Unhandled
'Unhandled': function() {
console.log("UNHANDLED");
var reprompt = ' That was not an appropriate response. Please say a number between 1 and 50.';
this.emit(':ask', reprompt, reprompt);
}
});
This is an example of the fifth request. You'll have 6 identical states like this one that string back to back. Eventually you'll end up with 6 session values.
this.attributes['NUMBERONE']
this.attributes['NUMBERTWO']
this.attributes['NUMBERTHREE']
this.attributes['NUMBERFOUR']
this.attributes['NUMBERFIVE']
this.attributes['NUMBERSIX']
You can then use these values for your game.
If you have not used the alexa-sdk before you must remember to register your state handlers and add your modes to the states variable.
alexa.registerHandlers(newSessionHandlers, NumberInputOneStateHandlers, ... NumberInputSixStateHandlers);
var states = {
NUMONEMODE: '_NUMONEMODE',
...
...
NUMSIXMODE: '_NUMSIXMODE',
}
This answer is not intended to cover the basics of coding using Alexas-SDK. There are other resourced for more specific questions on that topic.
Alternatively, because your intent is identical [GetNumberIntent], you may be able to get by with a single StateHandler that pushes new valid numbers onto an array until the array is the desired length. That would simply require more logic inside the Intent Handler and a conditional to break out of the state once the array is of length 6.
Try the code above first because it's easier to see the different states.
Related
I took this to the Godot Reddit first, but honestly it's not much help imo. A lot of questions go unanswered there. So here I am.
As the title says, Im making a quest system in godot 2d using nested dictionaries. Ive seen people use Nodes, Classes or otherwise for their quest systems, and a few dictionary based ones out there. I chose dictionaries as I know them the best(still isnt a whole lot, or i probably wouldnt be here asking this lol) And my quest system is set up like so:
QuestBase --> QuestHandler --> PlayerData
QuestBase is a giant dictionary of all available quests in the game.
PlayerData is a giant dictionary of all the player stats(including active, completed, or failed quests)
and QuestHandler takes a questName in a function to Copy the quest(questName) from QuestBase dict into the PlayerData.quests_active dict.(quests_active is a dictionary of quests(also dictionaries) inside of the PlayerData dictionary lol) But i cant seem to get it to work. I've done it a couple different ways now, including the way the Godot documentation states on how to add Dinctionaries into dictionaries. Please help, this is the error I get from QuestHandler:
Invalid set index 'tutorialQuest' (on base: 'Nil') with value of type 'Dictionary'
QuestBase:
var storyQuests = {
"tutorialQuest":{
"name" : "Your First Steps",#Name of the Quest
"desc" : "Get to know your environment and learn the ropes.",#Description of the Quest
"level" : 1, #Required level before player can accept quest
"questType" : 0, #0=Story Quest || 1=Side Quest || 2=Repeateable Quest
"taskType": 0, #0=Progressive(task must be completed in order) || 1=Synchronous(tasks can be completed in any order, and all at once)
"tasks":{#Dictionary of Quest's Tasks
"task1":{
"text":"Talk to Bjorn",#Text to display in Quest log
"type":0,#0=Talk,1=Slay,2=Fetch,3=Deliver,4=Collect,5=Goto
"quantity":0,#Determines the amount to complete task for Slay, Fetch, and Collect
"target":"Bjorn",#Determines Who to talk to, or who to slay, or what to collect.
"completed":false#Is this task complete?
},
"task2":{
"text":"Talk to Bjorn",
"type":"Talk",
"target":"Bjorn",
"completed":false
},
"task3":{
"text":"Talk to Bjorn",
"type":"Talk",
"target":"Bjorn",
"completed":false
}
},
"itemReward":{
"items":["Sword", "Basic Elixer"],#Names of items to give
"amount":[1, 5]#Amount of each item to give, respectively.
},
"expReward":{
"skill":["Forestry","Smithing"],#Names of skills to add Exp to
"amount": [100,100] #Amount to add to each skill, respectively.
#1st number will increase first skill in "skill", etc...
},
"Complete":false,
"Failed":false
}
}
PlayerData:
var playerData = {
... irrelevant player data...
#Quests
"quests_active": {"blank":"blank"},
"quests_completed": {},
"quests_failed": {},
and Finally, Quest Handler:
func startQuest(questName: String):
var active_quests = PlayerData.playerData.get("quests_active")
if QuestBase.storyQuests.has(questName):
var questCopy = QuestBase.storyQuests.get(questName).duplicate(true)
PlayerData.playerData.get("quests_active")[questName] = questCopy #.quests_active.append(questCopy)
#print("Story Quest Started: " + PlayerData.playerData.QuestsActive.get(questName).get("name"))
elif QuestBase.sideQuests.has(questName):
var questCopy = QuestBase.sideQuests.get(questName).duplicate(true)
active_quests.append(questCopy)
#print("Side Quest Started: " + PlayerData.playerData.QuestsActive.get(questName).get("name"))
else:
print("Quest Doesn't Exist! Check Spelling!")
I'm building complications for a nutrition tracking app. I'd like to use offer multiple smaller complications, so the user can track their nutrition.
EG:
'MyApp - Carbohydrates'
'MyApp - Protein'
'MyApp - Fat'
this way on the Modular watch face, they could track all three by using the three bottom 'modular small' complications.
I'm aware this can be achieved by only offering larger sizes that can display everything at once (eg the 'modular large' complication), but I'd like to offer the user choice about how they set up their watch face.
I can't see a way to offer multiple of the same complication, is there any way around this?
The previous answer is outdated. WatchOS 7 onwards, we can now add multiple complications to the same complication family for our app.
Step 1:
In our ComplicationController.swift file, we can make use of the getComplicationDescriptors function, which allows us to describe what complications we are making available in our app.
In the descriptors array, we can append one CLKComplicationDescriptor() for each kind of complication per family that we want to build.
func getComplicationDescriptors(
handler: #escaping ([CLKComplicationDescriptor]) -> Void) {
var descriptors: [CLKComplicationDescriptor] = []
for progressType in dataController.getProgressTypes() {
var dataDict = Dictionary<AnyHashable, Any>()
dataDict = ["id": progressType.id]
// userInfo helps us know which type of complication was interacted with by the user
let userActivity = NSUserActivity(
activityType: "org.example.foo")
userActivity.userInfo = dataDict
descriptors.append(
CLKComplicationDescriptor(
identifier: "\(progressType.id)",
displayName: "\(progressType.title)",
supportedFamilies: CLKComplicationFamily.allCases, // you can replace CLKComplicationFamily.allCases with an array of complication families you wish to support
userActivity: userActivity)
)
}
handler(descriptors)
}
The app will now have multiple complications (equal to the length of the dataController.getProgressTypes() array) for each complication family that you support.
But how do you now display different data and views for different complications?
Step 2:
In the getCurrentTimelineEntries and getTimelineEntries functions, we can then make use of the complication.identifier value to identify the data that was passed along when this complication entry was called for.
Example, in the getTimelineEntries function:
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: #escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries after the given date
var entries: [CLKComplicationTimelineEntry] = []
...
...
...
var next: ProgressDetails
// Find the progressType to show using the complication identifier
if let progressType = dataController.getProgressAt(date: current).first(where: {$0.id == complication.identifier}) {
next = progressType
} else {
next = dataController.getProgressAt(date: current)[0] // Default to the first progressType
}
let template = makeTemplate(for: next, complication: complication)
let entry = CLKComplicationTimelineEntry(
date: current,
complicationTemplate: template)
entries.append(entry)
...
...
...
handler(entries)
}
You can similarly find the data that is passed in the getCurrentTimelineEntry and the getLocalizableSampleTemplate functions.
Step 3:
Enjoy!
There is currently no way to create multiple complications in the same family (e.g. Modular Small, Modular Large, Utilitarian Small etc.).
You could offer a way for the user to customize each complication to display Carbohydrates, Protein and Fat. You could even display different data for each complication family, but as far as having, for example, 2 modular small complications displaying different data, it is not possible yet.
You can see this if you put 2 of the same built in Apple complications in different places on your watchface they display the same thing. If Apple isn't even doing it with their own complications then it is more than likely impossible. Hope that explanation helps.
I am trying to read and parse and excel and some unclear things come into play as usual for me.
Here is what i have:
while (true)
{
comVariantCell1 = cells.item(row, 1).value().variantType();
comVariantCell2 = cells.item(row, 2).value().variantType();
//if an empty cell is found, processing will stop and user will get an error message in order to solve the inconsistency.
if (comVariantCell1 != COMVariantType::VT_EMPTY && comVariantCell2 != COMVariantType::VT_EMPTY)
{
//both cells have values, check their types.
importedLine = conNull();
progress1.setText(strfmt("Importing row %1", row));
if (cells.item(row, 1).value().variantType() == COMVariantType::VT_BSTR)
{
importedLine += cells.item(row, 1).value().bStr();
}
else
{
importedLine += cells.item(row, 1).value().double();
}
importedLine += cells.item(row, 2).value().double();
importedLinesCollection += [importedLine]; //conIns(importedLinesCollection, row - 1, (importedLine));
row++;
}
else
{
info (strFmt("Empty cell found at line %1 - import will not continue and no records were saved.", row));
break;
}
}
Excel format:
Item number Transfer Qty
a100 50.5
a101 10
a102 25
This worked well to check if the cell type is string: COMVariantType::VT_BSTR
but what should i use to check for a real or integer value ?
I am pretty sure in this case, the quantity will be not contain real values but anyway, it could be useful in the future to make the difference between these two types.
I have to mention that, even if i have an int value and I use cells.item(row, 1).value().int() it won't work. I can't see why.
Why do i want to make the difference? Because if it's forbidden to have real values in the quantity column ( at least in my case ), i want to check that and give the user the opportunity to put a correct value in that place and maybe further investigate why that happened to be there.
Take a look on how it is done in \Classes\SysDataExcelCOM\readRow.
It is basically using switch to test the type. This is really boring!
Also take a look on ExcelIO, a class I made some years ago. It reads Excel and returns each row as a container. This is a more high-level approach.
As a last resort you could save the Excel as a tab separated file. Then use TextIO to read the content. This will be at least 10 times faster than using Excel!
Is there a way to wait a set amount of time between tests? I need a solution to compensate for server lag. When creating a record, it takes a little bit of time before the record is searchable in my environment.
In the following code example, how would I wait 30 seconds between the first test and the second test and have no wait time between second test and third test?
class MySpec extends GebReportingSpec {
// First Test
def "should create a record named myRecord"() {
given:
to CreateRecordsPage
when:
name_field = "myRecord"
and:
saveButton.click()
then:
at IndexPage
}
// Second Test
def "should find record named myRecord"() {
given:
to SearchPage
when:
search_query = "myRecord"
and:
searchButton.click()
then:
// haven't figured this part out yet, but would look for "myRecord" on the results page
}
// Third Test
def "should delete the record named myRecord"() {
// do the delete
}
}
You probably don't want to wait a set amount of time - it will make your tests slow. You would ideally want to continue as soon as the record is added. You can use Geb's waitFor {} to poll for a condition to be fulfilled.
// Second Test
def "should find record named myRecord"() {
when:
to SearchPage
then:
waitFor(30) {
search_query = "myRecord"
searchButton.click()
//verify that the record was found
}
}
This will poll every half a second for 30 seconds for the condition to be fulfilled passing as soon as it is and failing if it's still not fulfilled after 30 seconds.
To see what options you have for setting waiting time and interval have look at section on waiting in The Book of Geb. You might also want to check out the section on implicit assertions in waitFor blocks.
If your second feature method depends on success of the first one then you should probably consider annotating this specification with #Stepwise.
You should always try to use waitFor and check conditions wherever possible. However if you find there isn't a specific element you can check for, or any other condition to check, you can use this to wait for a specified amount of time:
def sleepForNSeconds(int n) {
def originalMilliseconds = System.currentTimeMillis()
waitFor(n + 1, 0.5) {
(System.currentTimeMillis() - originalMilliseconds) > (n * 1000)
}
}
I had to use this while waiting for some chart library animations to complete before capturing a screenshot in a report.
Thread.sleep(30000)
also does the trick. Of course still agree to "use waitFor whenever possible".
We're looking into refining our User Groups in Dynamics AX 2009 into more precise and fine-tuned groupings due to the wide range of variability between specific people within the same department. With this plan, it wouldn't be uncommon for majority of our users to fall user 5+ user groups.
Part of this would involve us expanding the default length of the User Group ID from 10 to 40 (as per Best Practice for naming conventions) since 10 characters don't give us enough room to adequately name each group as we would like (again, based on Best Practice Naming Conventions).
We have found that the main information seems to be obtained from the UserGroupInfo table, but that table isn't present under the Data Dictionary (it's under the System Documentation, so unavailable to be changed that way by my understanding). We've also found the UserGroupName EDT, but that is already set at 40 characters. The form itself doesn't seem to restricting the length of the field either. We've discussed changing the field on the SQL directly, but again my understanding is that if we do a full synchronization it would overwrite this change.
Where can we go to change this particular setting, or is it possible to change?
The size of the user group id is defined as as system extended data type (here \System Documentation\Types\userGroupId) and you cannot change any of the properties including the size 10 length.
You should live with that, don't try to fake the system using direct SQL changes. Even if you did that, AX would still believe that length is 10.
You could change the SysUserInfo form to show the group name only. The groupId might as well be assigned by a number sequence in your context.
I wrote a job to change the string size via X++ and it works for EDTs, but it can't seem to find the "userGroupId". From the general feel of AX I get, I'd be willing to guess that they just have it in a different location, but maybe not. I wonder if this could be tweaked to work:
static void Job9(Args _args)
{
#AOT
TreeNode treeNode;
Struct propertiesExt;
Map mapNewPropertyValues;
void setTreeNodePropertyExt(
Struct _propertiesExt,
Map _newProperties
)
{
Counter propertiesCount;
Array propertyInfoArray;
Struct propertyInfo;
str propertyValue;
int i;
;
_newProperties.insert('IsDefault', '0');
propertiesCount = _propertiesExt.value('Entries');
propertyInfoArray = _propertiesExt.value('PropertyInfo');
for (i = 1; i <= propertiesCount; i++)
{
propertyInfo = propertyInfoArray.value(i);
if (_newProperties.exists(propertyInfo.value('Name')))
{
propertyValue = _newProperties.lookup(propertyInfo.value('Name'));
propertyInfo.value('Value', propertyValue);
}
}
}
;
treeNode = TreeNode::findNode(#ExtendedDataTypesPath);
// This doesn't seem to be able to find the system type
//treeNode = treeNode.AOTfindChild('userGroupId');
treeNode = treeNode.AOTfindChild('AccountCategory');
propertiesExt = treeNode.AOTgetPropertiesExt();
mapNewPropertyValues = new Map(Types::String, Types::String);
mapNewPropertyValues.insert('StringSize', '30');
setTreeNodePropertyExt(propertiesExt, mapNewPropertyValues);
treeNode.AOTsetPropertiesExt(propertiesExt);
treeNode.AOTsave();
info("Done");
}