Best way to parse commands in a java text-based game - java

I'm developing a text based game in java and I'm looking for the best way to deal with player's commands. Commands allow the player to interact with the environment, like :
"look north" : to have a full description of what you have in the north direction
"drink potion" : to pick an object named "potion" in your inventory and drink it
"touch 'strange button'" : touch the object called 'strange button' and trigger an action if there is one attached to it, like "oops you died..."
"inventory" : to have a full description of your inventory
etc...
My objective is now to develop a complete set of those simple commands but I'm having trouble to find an easy way to parse it. I would like to develop a flexible and extensible parser which could call the main command like "look", "use", "attack", etc... and each of them would have a specific syntax and actions in the game.
I found a lot of tools to parse command line arguments like -i -v --verbose but none of them seems to have the sufficient flexibility to fit my needs. They can parse one by one argument but without taking into account a specific syntax for each of them. I tried JCommander which seems to be perfect but I'm lost between what is an argument, a parameter, who call who, etc...
So if someone could help me to pick the correct java library to do that, that would be great :)

Unless you're dealing with complex command strings that involve for instance arithmetic expressions or well balanced parenthesis I would suggest you go with a plain Scanner.
Here's an example that I would find readable and easy to maintain:
interface Action {
void run(Scanner args);
}
class Drink implements Action {
#Override
public void run(Scanner args) {
if (!args.hasNext())
throw new IllegalArgumentException("What should I drink?");
System.out.println("Drinking " + args.next());
}
}
class Look implements Action {
#Override
public void run(Scanner args) {
if (!args.hasNext())
throw new IllegalArgumentException("Where should I look?");
System.out.println("Looking " + args.next());
}
}
And use it as
Map<String, Action> actions = new HashMap<>();
actions.put("look", new Look());
actions.put("drink", new Drink());
String command = "drink coke";
// Parse
Scanner cmdScanner = new Scanner(command);
actions.get(cmdScanner.next()).run(cmdScanner);
You could even make it fancier and use annotations instead as follows:
#Retention(RetentionPolicy.RUNTIME)
#interface Command {
String value();
}
#Command("drink")
class Drink implements Action {
...
}
#Command("look")
class Look implements Action {
...
}
And use it as follows:
List<Action> actions = Arrays.asList(new Drink(), new Look());
String command = "drink coke";
// Parse
Scanner cmdScanner = new Scanner(command);
String cmd = cmdScanner.next();
for (Action a : actions) {
if (a.getClass().getAnnotation(Command.class).value().equals(cmd))
a.run(cmdScanner);
}

I don't think you want to parse command line arguments. That would mean each "move" in your game would require running a new JVM instance to run a different program and extra complexity of saving state between JVM sessions etc.
This looks like a text based game where you prompt users for what to do next. You probably just want to have users enter input on STDIN.
Example, let's say your screen says:
You are now in a dark room. There is a light switch
what do you want to do?
1. turn on light
2. Leave room back the way you came.
Please choose option:
then the user types 1 or 2 or if you want to be fancy turn on light etc. then you readLine() from the STDIN and parse the String to see what the user chose. I recommend you look at java.util.Scannerto see how to easily parse text
Scanner scanner = new Scanner(System.in);
String userInput = scanner.readLine();
//parse userInput string here

the fun part of it is to have some command is human readable, which at the same time, it's machine parsable.
first of all, you needs to define the syntax of your language, for example:
look (north|south|east|west)
but it's in regular expression, it's generally speaking not a best way to explain a syntactical rule, so i would say this is better:
Sequence("look", Xor("north", "south", "east", "west"));
so by doing this, i think you've got the idea. you need to define something like:
public abstract class Syntax { public abstract boolean match(String cmd); }
then
public class Atom extends Syntax { private String keyword; }
public class Sequence extends Syntax { private List<Syntax> atoms; }
public class Xor extends Syntax { private List<Syntax> atoms; }
use a bunch of factory functions to wrap the constructors, returning Syntax. then you will have something like this eventually:
class GlobeSyntax
{
Syntax syntax = Xor( // exclusive or
Sequence(Atom("look"),
Xor(Atom("north"), Atom("south"), Atom("east"), Atom("west"))),
Sequence(Atom("drink"),
Or(Atom("Wine"), Atom("Drug"), Atom("Portion"))), // may drink multiple at the same time
/* ... */
);
}
or so.
now what you need is just a recursive parser according to these rules.
you can see, it's recursive structure, very easy to code up, and very easy to maintain. by doing this, your command is not only human readable, but machine parsable.
sure it's not finished yet, you needs to define action. but it's easy right? it's typical OO trick. all to need to do is to perform something when Atom is matched.

Related

How do I track variable dependencies in Nashorn?

I would like to use the Nashorn engine as a general computation engine. It is powerful, fast has plenty of built-in functions and new functions are very easy to add, using #FunctionalInterface or static methods. Even better, it also provides value-adds like cyclic dependency checking, syntax checking, etc.
However I need to automatically update "output" variables when a dependency changes.
The general idea is that in Java, I'll have something like:
class CalculationEngine {
Data addData(String name, Number value){
...
}
Data addData(String name, String formula){
...
}
String getScript(){
...
}
}
CalculationEngine engine = new CalculationEngine();
Data datum1 = engine.addData("datum1", 1); // Constant integer 1
Data datum2 = engine.addData("datum2", 2); // Constant integer 2
Data datum3 = engine.addData("datum3", "datum1*10");
Data datum4 = engine.addData("datum4", "datum3+datum2");
The CalculationEngine service class knows how to use Nashorn to create a script string out of the Data objects that looks like this:
final String script = engine.getScript(); // "var datum1=1; var datum2=2; var datum3=datum1*10; var datum4=datum3+datum2;"
I know I can parse the script with the Nashorn Parser:
final CompilationUnitTree tree = parser.parse("test", script, null);
But how do I extract the dependencies:
List<Data> whatDependsOn(Data input){
// Process the parsed tree
return list;
}
such that whatDependsOn(datum2) returns [datum4] and whatDependsOn(datum1) returns [datum3, datum4] ?
Or the inverse function getReferencedVariables such that getReferencedVariables(datum3) returns [datum1] and getReferencedVariables(datum4) returns [datum2, datum3] (and I can recursively query getReferencedVariables until all referenced variables have been found).
Basically, when the "value" of one of my Data objects change (due to an external event), how I determine which of my script formulae are affected and need to be recomputed?
I know that the Nashorn script can be parsed but I can not figure out how to use the SimpleTreeVisitorES6 to build up a variable dependency graph:
final CompilationUnitTree tree = parser.parse("test", script, null);
if (tree != null) {
tree.accept(new SimpleTreeVisitorES6<Void, Void>() {
#Override
public Void visitVariable(VariableTree tree, Void v) {
final Kind kind = tree.getKind();
System.out.println("Found a variable: " + kind);
System.out.println(" name: " + kind.toString());
IdentifierTree binding = (IdentifierTree) tree.getBinding();
System.out.println(" kind: " + binding.getKind().name());
System.out.println(" name: " + binding.getName());
System.out.println(" val: " + kind.name());
return null;
}
}, null);
}
one of Nashorn devs here. What you are trying to do is compute the so called def-use relations on source code (well, more likely their transitive closure, but I digress). That's a well-understood compiler theory concept. The good news is that CompilationUnitTree and friends should give you enough information to implement an algorithm for computing this information. The bad news is you'll have to roll up your sleeves and roll your own implementation, I'm afraid. You'll basically have to gather this information, produce merges at control flow join points (back edges and exits of loops, ends of if statements, but you'll also have to handle more exotic stuff like switch/case with their fallthrough semantics and also try/catch/finally, which is the least fun of these as basically control can transfer from anywhere in try block to a catch block.) Your algorithm will also have to repeatedly evaluate loop bodies until the static information you're gathering reaches a fixpoint.
FWIW, while writing Nashorn I had to implement these kinds of things few times using Nashorn's internal parser API (which is different but similar to the public one). If you want some inspiration, you can look into the source code for Nashorn static type analyzer for inferring types of local variables in a JavaScript function which is something I wrote some years ago. If nothing else, it'll give you an idea how to walk an AST tree and keep track of control flow edges and partially computed static analysis data at the edges.
I wish there were an easier way to do this… FWIW, a generalized static analyzer that helps you with bookeeping of flow control could be possible. Good luck.

Enum toString sometimes replacing i with ı

I recently got a report that a few Google Analytics event category names were being recorded with an i character with out a dot on top.
Pageviews and events occurring twice, once without dots over the i.
I had to look to believe it. Sure enough, I had an event called favorite and there was a handful called favorıte. Copy and paste that weird character into a terminal or a monospace font just to see how weird it is. favorıte
My first suspicion is my code where I generate the strings for the category names using toString on an enum.
public enum AnalyticsEvent {
SCREEN_VIEW,
FAVORITE,
UN_FAVORITE,
CLICK_EVENT,
... reduced for brevity;
public String val() {
return this.toString().toLowerCase();
}
}
Example of how that enum is used:
#Override
public void logSearchTag(String type, String value) {
...
logGAEvent(AnalyticsEvent.SEARCH_TAG.val(), type, value);
}
private void logGAEvent(String category, String action, String label) {
... // mGATracker = instance of com.google.android.gms.analytics.Tracker;
mGATracker.send(addCustomDimensions(new HitBuilders.EventBuilder()
.setCategory(category)
.setAction(action)
.setLabel(label))
.build());
...
}
I am going to solve this by actually assigning a string to the enums and instead return that in the val() function.
Though, I am curious if anyone knows why on a small handful of devices Enum.toString returns the enum name with that weird character replacing the i. I mean small. 8 out 50,000 is the average. Or is it possible that assumption is wrong and the error is on analytics service end somewhere? Really highly doubt that.
The String#toLowerCase method uses the default locale of the system. This use locale specific characters such as ı instead of i. In order to fix this problem call toLowerCase with a locale:
String test = "testString";
test.toLowerCase(java.util.Locale.ENGLISH) // Or your preferred locale

How to compare a element in String array in Android?

I have a String array as follows:
String [] str_cmd_arr={"cmd1", "cmd2"};
Given that, "cmd1" will output "perform command 1", while "cmd2" will output "perform command 2".
From my str_cmd_arr, how can I print the outputs individually in Java/Android? Currently, I am using this code
for (int i=0;i<str_cmd_arr.length;i++){
if(i<1){
Log.d("TAG","perform command 1");
}
else{
Log.d("TAG","perform command 2");
}
}
The real solution here: use a Map, like
Map<String, String> commandsAndOutput = new HashMap<>();
commandsAndOutput.put("cmd1", "cmd1 output");
...
to later do
String output = commandsAndOutput.get("cmd1");
for example.
Another, probably more sane way here: consider using enums, like:
public enum Command {
CMD1, CMD2;
}
if you are looking for more "compile time" support when making choices between different commands. As you now can write down:
Command cmd = ...
switch(cmd) {
case(CMD1) : ...
But another word of warning: one should be careful about such enum/switching code. In most situations, a "real OO based" design that works with an abstract base class Command and specific subclasses is the better choice.
The real lesson here: you want to study some basics, like the tutorials found here. You see, there is no point in programming for Android ... if you don't know about such basic things such as Maps. In that sense it is hard to give you "good" advise, as the "good" stuff is that abstract base class solution - which seems to be completely beyond your current skills.
Replace your if statement with
if(str_cmd_arr[i]).equals("cmd1"){
You can use a loop and a switch statement
for example
for (int i=0;i<str_cmd_arr.length;i++){
switch(str_cmd_arr[i]) {
case "cmd1":
Log.d("TAG","perform command 1");
break;
case "cmd2":
Log.d("TAG","perform command 2");
break;
}
}

Stanford-NER customization to classify software programming keywords

I am new in NLP and I used Stanford NER tool to classify some random text to extract special keywords used in software programming.
The problem is, I don't no how to do changes to the classifiers and text annotators in Stanford NER to recognize software programming keywords. For example:
today Java used in different operating systems (Windows, Linux, ..)
the classification results should such as:
Java "Programming_Language"
Windows "Operating_System"
Linux "Operating_system"
Would you please help on how to customize the StanfordNER classifiers to satisfied my needs?
I think it is quite well documented in Stanford NER faq section http://nlp.stanford.edu/software/crf-faq.shtml#a.
Here are the steps:
In your properties file change the map to specify how your training data is annotated (or
structured)
map = word=0,myfeature=1,answer=2
In src\edu\stanford\nlp\sequences\SeqClassifierFlags.java
Add a flag stating that you want to use your new feature, let's call it useMyFeature
Below public boolean useLabelSource = false , Add
public boolean useMyFeature= true;
In same file in setProperties(Properties props, boolean printProps) method after
else if (key.equalsIgnoreCase("useTrainLexicon")) { ..} tell tool, if this flag is on/off for you
else if (key.equalsIgnoreCase("useMyFeature")) {
useMyFeature= Boolean.parseBoolean(val);
}
In src/edu/stanford/nlp/ling/CoreAnnotations.java, add following
section
public static class myfeature implements CoreAnnotation<String> {
public Class<String> getType() {
return String.class;
}
}
In src/edu/stanford/nlp/ling/AnnotationLookup.java in
public enumKeyLookup{..} in bottom add
MY_TAG(CoreAnnotations.myfeature.class,"myfeature")
In src\edu\stanford\nlp\ie\NERFeatureFactory.java, depending on the
"type" of feature it is, add in
protected Collection<String> featuresC(PaddedList<IN> cInfo, int loc)
if(flags.useRahulPOSTAGS){
featuresC.add(c.get(CoreAnnotations.myfeature.class)+"-my_tag");
}
Debugging:
In addition to this, there are methods which dump the features on file, use them to see how things are getting done under hood. Also, I think you would have to spend some time with debugger too :P
Seems you want to train your custom NER model.
Here is a detailed tutorial with full code:
https://dataturks.com/blog/stanford-core-nlp-ner-training-java-example.php?s=so
Training data format
Training data is passed as a text file where each line is one word-label pair. Each word in the line should be labeled in a format like "word\tLABEL", the word and the label name is separated by a tab '\t'. For a text sentence, we should break it down into words and add one line for each word in the training file. To mark the start of the next line, we add an empty line in the training file.
Here is a sample of the input training file:
hp Brand
spectre ModelName
x360 ModelName
home Category
theater Category
system 0
horizon ModelName
zero ModelName
dawn ModelName
ps4 0
Depending upon your domain, you can build such a dataset either automatically or manually. Building such a dataset manually can be really painful, tools like a NER annotation tool can help make the process much easier.
Train model
public void trainAndWrite(String modelOutPath, String prop, String trainingFilepath) {
Properties props = StringUtils.propFileToProperties(prop);
props.setProperty("serializeTo", modelOutPath);
//if input use that, else use from properties file.
if (trainingFilepath != null) {
props.setProperty("trainFile", trainingFilepath);
}
SeqClassifierFlags flags = new SeqClassifierFlags(props);
CRFClassifier<CoreLabel> crf = new CRFClassifier<>(flags);
crf.train();
crf.serializeClassifier(modelOutPath);
}
Use the model to generate tags:
public void doTagging(CRFClassifier model, String input) {
input = input.trim();
System.out.println(input + "=>" + model.classifyToString(input));
}
Hope this helps.

How to use an array value as field in Java? a1.section[2] = 1;

New to Java, and can't figure out what I hope to be a simple thing.
I keep "sections" in an array:
//Section.java
public static final String[] TOP = {
"Top News",
"http://www.mysite.com/RSS/myfeed.csp",
"top"
};
I'd like to do something like this:
Article a1 = new Article();
a1.["s_" + section[2]] = 1; //should resolve to a1.s_top = 1;
But it won't let me, as it doesn't know what "section" is. (I'm sure seasoned Java people will cringe at this attempt... but my searches have come up empty on how to do this)
Clarification:
My article mysqlite table has fields for the "section" of the article:
s_top
s_sports
...etc
When doing my import from an XML file, I'd like to set that field to a 1 if it's in that category. I could have switch statement:
//whatever the Java version of this is
switch(section[2]) {
case "top": a1.s_top = 1; break;
case "sports": a1.s_sports = 1; break;
//...
}
But I thought it'd be a lot easier to just write it as a single line:
a1["s_"+section[2]] = 1;
In Java, it's a pain to do what you want to do in the way that you're trying to do it.
If you don't want to use the switch/case statement, you could use reflection to pull up the member attribute you're trying to set:
Class articleClass = a1.getClass();
Field field = articleClass.getField("s_top");
field.set(a1, 1);
It'll work, but it may be slow and it's an atypical approach to this problem.
Alternately, you could store either a Map<String> or a Map<String,Boolean> inside of your Article class, and have a public function within Article called putSection(String section), and as you iterate, you would put the various section strings (or string/value mappings) into the map for each Article. So, instead of statically defining which sections may exist and giving each Article a yes or no, you'd allow the list of possible sections to be dynamic and based on your xml import.
Java variables are not "dynamic", unlink actionscript for exemple. You cannot call or assign a variable without knowing it at compile time (well, with reflection you could but it's far to complex)
So yes, the solution is to have a switch case (only possible on strings with java 1.7), or using an hashmap or equivalent
Or, if it's about importing XML, maybe you should take a look on JAXB
If you are trying to get an attribute from an object, you need to make sure that you have "getters" and "setters" in your object. You also have to make sure you define Section in your article class.
Something like:
class Article{
String section;
//constructor
public Article(){
};
//set section
public void setSection(Section section){
this.section = section;
}
//get section
public String getSection(){
return this.section;
}

Categories

Resources