I'm making a text-based adventure game in Java, and I want to be able to match the player location with the location of a character/enemy. My list of characters is imported from a text file and put into a Hashmap. The importing from a text file is a requirement.
I can match the location if I specify the value (name) of the character, but I want to be able to have it go through and match on the "location" property of the character. Here is what I have:
Character object:
Character(String name, String location, int maxHp, int maxAttackDmg, String description)
Character HashMap:
HashMap<String, Character> characters = ReadIn.createCharacters();
ReadIn.createCharacters() parses a text file for the character properties.
There is another method called player.getLocation() which gives the player's current location.
Here is what I have working:
if (player.getLocation().equals(skeleton.getLocation()) && skeleton.getDefeated() == false) {
Encounter e = new Encounter();
e.fight(player,skeleton);
}
If the player is in the same location as the character, and the character has not been defeated, then call the fight() method.
What I want to do is this:
if (player.getLocation().equals(any location in the character HashMap) {
Encounter e = new Encounter();
e.fight(player,<matched character from HashMap>);
}
I know what I want to do, I just don't know how to do it in Java. I'm quite new to Java and programming in general. Hopefully I gave enough detail, but I can provide more if needed. Any help would be greatly appreciated!
The traditional way to achieve this in Java would be:
for (Character character: characters.values()) {
if (character.getLocation().equals(player.getLocation())
&& !character.getName().equals(player.getName())
&& !character.isDefeated()) {
... fight ...
}
}
The alternative using streams would be:
characters.values().stream()
.filter(ch -> ch.getLocation().equals(player.getLocation()))
.filter(ch -> !ch.getName().equals(player.getName()))
.filter(ch -> !ch.isDefeated())
.forEach(ch -> ... fight ...);
If you want a single encounter irrespective of the number of characters with the same location then replace forEach with
.findAny()
.ifPresent(ch -> ... fight ...);
I assume that your characters map is a "location to character map"
then use this
Character matchingCharacter = characters.get(player.getLocation());
if (matchingCharacter != null) {
Encounter e = new Encounter();
e.fight(player, matchingCharacter);
}
Related
The getText() returns the complete statement excluding the spaces between the words. One way of considering the spaces is to include them in grammar. But, is there any other way to get the complete String with the spaces considered.
Yes, there is (assuming here you are using ParserRuleContext.getText(). The idea is to ask the input char stream for a range of characters. The position values are stored in the start and stop tokens of the context.
Here's some code (converted from C++, so it might not be 100% correct):
string sourceTextForContext(ParseTree context) {
Token startToken = (context.start instanceof TerminalNode) ? (TerminalNode)(start).getSymbol() : (ParserRuleContext)(start).start;
Token stopToken = (context.stop instanceof TerminalNode) ? (TerminalNode)(stop).getSymbol() : (ParserRuleContext)(stop).start;
CharStream cs = start.getTokenSource().getInputStream();
int stopIndex = stop != null ? stop.getStopIndex() : -1;
return cs.getText(new Interval(start.getStartIndex(), stopIndex));
}
Since this retrieval function uses the absolute char indexes, it doesn't count in any possible whitespace rule.
I want to compare a string portion (i.e. character) against a Chinese character. I assume due to the Unicode encoding it counts as two characters, so I'm looping through the string with increments of two. Now I ran into a roadblock where I'm trying to detect the '兒' character, but equals() doesn't match it, so what am I missing ? This is the code snippet:
for (int CharIndex = 0; CharIndex < tmpChar.length(); CharIndex=CharIndex+2) {
// Account for 'r' like in dianr/huir
if (tmpChar.substring(CharIndex,CharIndex+2).equals("兒")) {
Also, feel free to suggest a more elegant way to parse this ...
[UPDATE] Some pics from the debugger, showing that it doesn't match, even though it should. I pasted the Chinese character from the spreadsheet I use as input, so I don't think it's a copy and paste issue (unless the unicode gets lost along the way)
oh, dang, apparently it does not work simply copy and pasting:
Use CharSequence.codePoints(), which returns a stream of the codepoints, rather than having to deal with chars:
tmpChar.codePoints().forEach(c -> {
if (c == '兒') {
// ...
}
});
(Of course, you could have used tmpChar.codePoints().filter(c -> c == '兒').forEach(c -> { /* ... */ })).
Either characters, accepting 兒 as substring.
String s = ...;
if (s.contains("兒")) { ... }
int position = s.indexOf("兒");
if (position != -1) {
int position2 = position + "兒".length();
s = s.substring(0, position) + "*" + s.substring(position2);
}
if (s.startsWith("兒", i)) {
// At position i there is a 兒.
}
Or code points where it would be one code point. As that is not really easier, variable substring seem fine.
if (tmpChar.substring(CharIndex,CharIndex+2).equals("兒")) {
Is your problem. 兒 is only one UTF-16 character. Many Chinese characters can be represented in UTF-16 in one code unit; Java uses UTF-16. However, other characters are two code units.
There are a variety of APIs on the String class for coping.
As offered in another answer, obtaining the IntStream from codepoints allows you to get a 32-bit code point for each character. You can compare that to the code point value for the character you are looking for.
Or, you can use the ICU4J library with a richer set of facilities for all of this.
If I have a parent string (let's call it output) that contains a list of variable assignments like so ...
status.availability-state available
status.enabled-state enabled
status.status-reason The pool is available
And I want to extract the values of each variable in that list given the variable names, ie the substring after the space following status.availability-state, status.enabled-state, and status.status-reason, such that I end up with three different variable assignments making each of the following String comparisons true ...
String availability = output.substring(TODO);
String enabled = output.substring(TODO);
String reason = output.substring(TODO);
availability.equals("available");
enabled.equals("enabled");
reason.equals("The pool is available");
What is the simplest way to do this? Should I even use substring for this?
This is a little tricky because you need to assign the value to a specific variable - you can't just have a map of keys to variables in Java.
I would consider doing this with a switch:
for (String line : output.split('\n')) {
String[] frags = line.split(' ', 2); // Split the line in 2 at the space.
switch (frags[0]) { // This is the "key" of the variable.
case "status.availability-state":
availability = frags[1]; // This assigns the "value" to the relevant variable.
break;
case "status.enabled-state":
enabled = frags[1];
break;
// ... etc
}
}
It's not very pretty, but you don't have too many options.
There seem to be two questions here -- how to parse the string, and how to assign to variables by name.
Tackle the string parsing one step at a time:
first write a program to read one line at a time and output each one in the body of a loop. String.split() or StringTokenizer are two options here.
next enhance this by writing a method to handle one line. The same tools are helpful here, to split on spaces.
You should now have a program that can print name: status.availability-state, value: available for each line of input.
Next, you're asking to programatically assign to variables based on the name of the parameter.
There is no legitimate way to look at a variable's name at runtime (OK, Java 8 reflection has ways, but it shouldn't be used without very good reason).
So, the best you can do is to use a switch or if statement:
switch(name) {
case status.availability-state:
availability = value;
break;
... etc.
}
However, whenever you use switch or if you should think about whether there's a better way.
Is there any reason you can't turn these variables into Map entries?
configMap.add(name,value);
Then to read it:
doSomethingWith(configMap.get("status.availability");
That's what maps are for. Use them.
This is a similar situation to the rookie mistake of using variables called person1, person2, person3... instead of using an array. Eventually they ask "How do I go from the number 25 to my variable person25?" -- and the answer is, you can't, but an array or list makes it easy. people[number] or people.get(number)
A valid alternative is to split the string by \n and add to a Map. Example:
String properties = "status.availability-state available\nstatus.enabled-state enabled\nstatus.status-reason The pool is available";
Map<String, String> map = Arrays.stream(properties.split("\n"))
.collect(Collectors.toMap(s -> s.split(" ")[0], s -> s.split(" ", 2)[1]));
System.out.println(map.get("status.status-reason"));
Should output The pool is available
This loop will match and extract the variables, and you can then assign them as you see fit:
Pattern regex = Pattern.compile("status\\.(.*?)-.*? ([a-z]+)");
Matcher matcher = regex.matcher(output);
while (matcher.find()) {
System.out.println(matcher.group(1) + "=" + matcher.group(2));
}
status\\. matches "status."
(.*?) matches any sequence of characters but isn't greedy, and captures them
-.* matches dash, any chars, space
([a-z]+) matches any string of lower-case letters, and captures them
Here's one way to do it:
Map<String, String> properties = getProperties(propertiesString);
availability = properties.get("availability-state");
enabled = properties.get("enabled-state");
reason = properties.get("status-reason");
// ...
public void getProperties(String input) {
Map<String, String> properties = new HashMap<>();
String[] lines = output.split("\n");
for (String line : lines) {
String[] parts = line.split(" ");
int keyStartIndex = parts[0].indexOf(".") + 1;
int spaceIndex = parts[1].indexOf(" ");
string key = parts[0].substring(keyStartIndex, spaceIndex);
properties.put(key, parts[1]);
}
return properties;
}
This seems to be a bit more straight-forward, in terms of the code that's setting these values, as each value is set to exactly the value from the map, rather than iterating over some list of strings and seeing if it contains a particular value and doing different things based on that.
This is designed with the primary use-case being that the string is created at runtime in memory. If the properties are created in an external file, this code would still work (after creating the desired String in memory), but it may be a better idea to use either a Properties file, or perhaps a Scanner.
So I am writing a scrabble word suggestion program that I decided to do because I wanted to learn sets (don't worry, I at least got that part) and referencing info/data not created within the program. Im pretty new to Java (and programming in general), but I was wondering how to pull words from a word list .FIC file in order to check them against words generated from the letters inputted.
To clarify, I have written a program which takes a series of letters and returns a set of every possible word created from those letters. for example:
input:
abc
would give a set containing the "words":
a, ab, ac, abc, acb, b, ba, bc, bac, bca, c, ca, cb, cab, cba
What I am asking, really, is how to check those to find the ones contained in the .FIC file.
The file is the "official crosswords" file from the Moby project word list and I am still (very) shaky on parsing and other file dealing-with methods. I am continuing to research so I dont have any prototype code for that.
Sorry if the question isn't entirely clear.
edit: here is the method that makes the "words" to make it easier to understand the idea. The part I don't understand is specifically how to pull a word(as a string) from the .FIC file.
private static Set<String> Words(String s)
{
Set<String> tempwords = new TreeSet<String>();
if (s.length() == 1)
{ // base case, last letter
tempwords.add(s);
// System.out.println(s); uncomment when debugging
}
else
{
//set up to add each letter in s
for (int i = 0; i < s.length(); i++)
{ //cut the i letter out of the string
String remaining = s.substring(0, i) + s.substring(i+1);
//recursion to add all combinations of letters onto the current letter/"word"
for (String permutation : Words(remaining))
{
// System.out.println(s.substring(i, i+1) + permutation); uncomment when debugging
//add the full length words
tempwords.add(s.substring(i, i+1) + permutation);
// System.out.println(permutation); uncomment when debugging
//add the not-full-length words
tempwords.add(permutation);
}
}
}
// System.out.println(tempwords); uncomment when debugging
return tempwords;
}
I dont know if it is the best solution, but i figured it out (hobbs the line thing helped a lot, thank you). I found that this works:
public static void main(String[] args) throws FileNotFoundException
{
Scanner s = new Scanner(new FileReader("C:/Users/Sean/workspace/Imbored/bin/113809of.fic"));
while(true)
{
words.clear();
String letters = enterLetters();
words.addAll(Words(letters));
while(s.hasNextLine()) {
String line = s.nextLine();
String finalword = checkWords(line, words);
if (finalword != null) finalwordset.add(finalword);
}
s.reset();
System.out.println(finalwordset);
System.out.println();
System.out.println("_________________________________________________________________________");
}
}
A few things:
The checkWords method checks if the current word from the file is in the generated list of "words"
The enterletters method takes user inputted letters and returns them in a string
The Words method returns a set of strings of all of the possible combinations of the characters in the given string, with each character used up to as many times as it appears in the string and no repeated "words" in the returned set.
finalwordset and words are arraylists of strings defined as instance variables(i would put them in the main method but I'm lazy and it doesn't matter for this case)
I am very sure there is a better/more efficient way to do this, but this at least works.
Finally: I decided to answer rather than delete because I didn't see this answered anywhere else, so if it is feel free to delete the question or link to the other answer or whatever, at this point it is to help other people.
I am writing a program that allows users to input variable names that they can then use in other Strings. For example, if the user enters:
$token aslkdjfna98y
A mapping is created for key "token" and value "aslkdjfna98y". I then want to add this token variable in a URL by specifying that it should be swapped out using this syntax:
http://www.example.com/getThing?token=$^{token}
So here, I would like to swap $^{token} with my mapped value aslkdjfna98y.
I have tried various String.replace and String.replaceAll calls, however I am currently getting stuck in a loop - where it's known that the String contains the text $^{token}, but I cannot replace the text. Here is where I am struggling:
if (request.contains("$^{"))
{
//handle variables
for (String s : variables.keySet())
{
String str = String.format(Locale.US, "$^{%s}", s);
while(request.contains(str))
{
//Stuck Here
request = request.replace(String.format(Locale.US, "$^{%s}", s), variables.get(s));
}
}
}
This could ideally be simplified down to:
request.replaceAll(regex, str);
How can I correctly replace the characters, or how can I improve this to use replaceAll?
Enclose the String in \\Q and \\E. This switches off all special characters in Java regexes:
request = request.replace(String.format(Locale.US, "\\Q$^{%s}\\E", s), variables.get(s));
"$^{token}"
im confused whats suppose to be in the token field...
any letters/num?
"$^{[a-zA-Z0-9]*}"
Um.....
a certain amount(8) of letters/numbs.
"$^{[a-zA-Z0-9]{8}}"
depending on the language you are using you might need to escape { $ and ^
I was able to simplify the code down to this simple block:
if (variables.get(s) != null) {
request = request.replaceAll(Log.format("\\Q$^{%s}\\E", s), variables.get(s));
}
else {
Log.err("No variable \"%s\" set", s);
}