How to parse dice notation with a Java regular expression? - java

Standard RPG dice notation is something like this: "AdB[x/]C[+-]D", where A, B, C, D are natural numbers: A the number of dice to roll, B the number of sides on each die, C a following multiplier or divisor, and D an addition or subtraction.
Some examples:
d6: Roll a single 6-sided die.
3d6: Roll 3 dice of 6-sides each and find the sum.
4d6+1: Roll 4 dice of 6-sides each, and add 1 to the sum.
3d6x10: Roll 3 dice of 6-sides each, then multiply the sum by 10.
d6/2: Roll a 6-sided die and then divide by 2 (rounding up).
I'd like a Java method that gets fed a string in this format and parses out the correct integer values of A, B, C, and D. (Say: If C is meant as a divisor, then store a negative int; likewise if D is meant as a subtraction do the same.) I've actually implemented this already with some loops looking at individual character values in the String, but it's ugly as sin.
I have the intuition that this would be more concisely solved by using a regular expression, but frankly I'm completely ignorant in that topic. Any suggestions on how that would be done? And if there's a more elegant abstraction to the problem (like say, an arbitrary number of separate x/+- modifiers; or needing to turn the x into *) I'd be open to that, too.
This question gives a closely related regular expression, but doesn't illustrate how to extract the parameters from it: Java Regex repetition (Dice notation parsing)

This isn't the smartest or most elegant way, but it's short, readable and robust. Since I'm not sure how you intend to use it I didn't add anything fancy for outputting or making this into an API/library:
public class DieRegex {
public static void main(String[] args) {
int amount, die, mult = 1, add = 0;
Pattern p = Pattern.compile("([1-9]\\d*)?d([1-9]\\d*)([/x][1-9]\\d*)?([+-]\\d+)?");
Matcher m = p.matcher("d20");
if (m.matches()) {
amount = (m.group(1) != null) ? Integer.parseInt(m.group(1)) : 1;
die = Integer.parseInt(m.group(2));
if (m.group(3) != null) {
boolean positive = m.group(3).startsWith("x");
int val = Integer.parseInt(m.group(3).substring(1));
mult = positive ? val : -val;
}
if (m.group(4) != null) {
boolean positive = m.group(4).startsWith("+");
int val = Integer.parseInt(m.group(4).substring(1));
add = positive ? val : -val;
}
}
else
System.out.println("No match"); // Do whatever you need
}
}
Note: According to the comments about checking the input:
amount (A) die (B) and mult (C) must be greater than 0. However, positive numbers with leading zeros are not allowed (02d20 is not valid, 2d20 is the correct form). This can be fixed with a lookahead but will make the regex more complicated.
add (D) can be any number (even with leading zeros).
mult (C) add (D) are 1 and 0 respectively if they don't exist in the string. It's just a default value that indicates "no value".
The format that is checked for:
A: [optional] non-negative number (default 1)
d
B: [required] non-negative number
C: [optional] / or x followed by a non-negative number (default 1)
D: [optional] + or - followed by a non-negative number (default 0)

This named-group example yields some promising results from your sample input.
Note: Requires Java 7 for named regex groups.
String pattern = "(?<A>\\d*)d((?<B>\\d+)(?<math>(?<mult>[x\\/](?<C>\\d+))?(?<add>[+-](?<D>\\d+))?)?)?";
Pattern p = Pattern.compile(pattern);
String[] tests = new String[] {
"d6", "3d6", "4d6+1", "3d6x10", "d6/2", "3d4/2-7", "12d4-", "d-8", "4dx"
};
for (String test : tests) {
System.out.printf("Testing \"%s\"\n", test);
Matcher m = p.matcher(test);
if (m.matches()) {
String groupA = m.group("A");
if (groupA == null) {
groupA = "1"; // default one roll
}
String groupB = m.group("B");
if (groupB == null) {
groupB = "6"; // default six-sided die
}
String groupC = m.group("C");
if (groupC == null) {
groupC = "1"; // default multiply or divide by 1
}
String groupD = m.group("D");
if (groupD == null) {
groupD = "0"; // default add or subtract 0
}
int a = Integer.parseInt(groupA);
int b = Integer.parseInt(groupB);
int c = Integer.parseInt(groupC);
int d = Integer.parseInt(groupD);
String groupMath = m.group("math");
if (groupMath != null && groupMath.isEmpty()) {
groupMath = null;
}
String groupAdd = m.group("add");
String groupMult = m.group("mult");
System.out.printf("A: %d\n", a);
System.out.printf("B: %d\n", b);
System.out.printf("C: %d\n", c);
System.out.printf("D: %d\n", d);
System.out.println("------");
System.out.printf("math: %s\n", groupMath);
System.out.printf("mult: %s\n", groupMult);
System.out.printf("add: %s\n", groupAdd);
} else {
System.out.println("No Match!");
}
System.out.println();
}
Output
Testing "d6"
A: 1
B: 6
C: 1
D: 0
------
math: null
mult: null
add: null
Testing "3d6"
A: 3
B: 6
C: 1
D: 0
------
math: null
mult: null
add: null
Testing "4d6+1"
A: 4
B: 6
C: 1
D: 1
------
math: +1
mult: null
add: +1
Testing "3d6x10"
A: 3
B: 6
C: 10
D: 0
------
math: x10
mult: x10
add: null
Testing "d6/2"
A: 1
B: 6
C: 2
D: 0
------
math: /2
mult: /2
add: null
Testing "3d4/2-7"
A: 3
B: 4
C: 2
D: 7
------
math: /2-7
mult: /2
add: -7
Testing "12d4-"
No Match!
Testing "d-8"
No Match!
Testing "4dx"
No Match!

I didn't extensively test it, but according to your explanation the following code works. May need some minor tweaking of the regex if you notice something's wrong. Biggest difference from this version to the rest of others is that it's modularized within a Class called RPGDice that you could instantiate with RPGDice.parse(expr) and get an RPGDice instance that contains the different attributes (rolls, faces, multiplier, additive).
public class RPGDice {
private static final Pattern DICE_PATTERN = Pattern.compile("(?<A>\\d*)d(?<B>\\d+)(?>(?<MULT>[x/])(?<C>\\d+))?(?>(?<ADD>[+-])(?<D>\\d+))?");
private int rolls = 0;
private int faces = 0;
private int multiplier = 0;
private int additive = 0;
public RPGDice(int rolls, int faces, int multiplier, int additive) {
this.rolls = rolls;
this.faces = faces;
this.multiplier = multiplier;
this.additive = additive;
}
public int getRolls() {
return rolls;
}
public int getFaces() {
return faces;
}
public int getMultiplier() {
return multiplier;
}
public int getAdditive() {
return additive;
}
#Override
public String toString() {
return String.format("{\"rolls\": %s, \"faces\": %s, \"multiplier\": %s, \"additive\": %s}", rolls, faces, multiplier, additive);
}
private static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
private static Integer getInt(Matcher matcher, String group, int defaultValue) {
String groupValue = matcher.group(group);
return isEmpty(groupValue) ? defaultValue : Integer.valueOf(groupValue);
}
private static Integer getSign(Matcher matcher, String group, String positiveValue) {
String groupValue = matcher.group(group);
return isEmpty(groupValue) || groupValue.equals(positiveValue) ? 1 : -1;
}
public static RPGDice parse(String str) {
Matcher matcher = DICE_PATTERN.matcher(str);
if(matcher.matches()) {
int rolls = getInt(matcher, "A", 1);
int faces = getInt(matcher, "B", -1);
int multiplier = getInt(matcher, "C", 1);
int additive = getInt(matcher, "D", 0);
int multiplierSign = getSign(matcher, "MULT", "x");
int additiveSign = getSign(matcher, "ADD", "+");
return new RPGDice(rolls, faces, multiplier * multiplierSign, additive * additiveSign);
}
return null;
// OR
// throw new IllegalArgumentException("Invalid Expression");
}
public static void main(String[] args) {
System.out.println(RPGDice.parse("d6"));
System.out.println(RPGDice.parse("d6x"));
System.out.println(RPGDice.parse("33d6x10"));
System.out.println(RPGDice.parse("336x10"));
System.out.println(RPGDice.parse("d6/"));
System.out.println(RPGDice.parse("d6/5"));
System.out.println(RPGDice.parse("d6/5+2"));
System.out.println(RPGDice.parse("2d6/5-32"));
System.out.println(RPGDice.parse("2d6/5+-32"));
}
}
Output:
{"rolls": 1, "faces": 6, "multiplier": 1, "additive": 0}
null
{"rolls": 33, "faces": 6, "multiplier": 10, "additive": 0}
null
null
{"rolls": 1, "faces": 6, "multiplier": -5, "additive": 0}
{"rolls": 1, "faces": 6, "multiplier": -5, "additive": 2}
{"rolls": 2, "faces": 6, "multiplier": -5, "additive": -32}
null

Simple Answer
Consider reading the manual.
RTxM1 and RTxM2
TLDR
the output of the code example below shows what you need to know to parse the dice notation using a regex.
public class RegEx
{
private static final int EXPECTED_GROUP_COUNT = 7;
private static final Pattern pattern = Pattern.compile("(\\d+)?[Dd](\\d+)([Xx/](\\d+))?(([+-])(\\d+))?");
private static void matchit(final String value)
{
final Matcher matcher = pattern.matcher(value);
if (matcher.matches())
{
final int groupCount;
final MatchResult matchResult = matcher.toMatchResult();
groupCount = matchResult.groupCount();
System.out.println("kapow: " + value + ", groups: " + groupCount);
if (groupCount == EXPECTED_GROUP_COUNT)
{
for (int index = 0; index <= groupCount; ++index)
{
final String currentGroup = matchResult.group(index);
System.out.println("\tgroup[" + index + "]: " + currentGroup);
}
}
else
{
System.out.println("match error; wrong group count");
}
}
else
{
System.out.println("Format not recognized: " + value);
}
}
public static void main(
String[] args)
{
final String[] thingArray =
{
"3d6",
"d7",
"4D6+4",
"3d6x10",
"d6/2"
};
for (final String thing : thingArray)
{
matchit(thing);
}
}
}

Related

Binary Search returning -1 in spite of the element existing

For this program, I'm trying to use Binary searching to find a specific element of a given array, such as title, year, or artist. For now, I'm only testing for title and year since they are both strings. But it seems that for some of the input I put in, the program would return -1, even though the input I put in exists on the array. I'm not sure why this happens.
First is the tester class, second code is the constructor class.
public class TestMusic
{
public static void printMusic(Music[] arr)
{
for (Music music : arr)
{
System.out.println(music.toString());
}
}
public static int binaryTitle(Music[] arr, String title)
{
int l = 0, r = arr.length - 1;
while (l <= r) {
int m = l + (r - l) / 2;
int res = title.compareTo(arr[m].getTitle());
// Check if x is present at mid
if (res == 0)
return m;
// If x greater, ignore left half
if (res > 0)
l = m + 1;
// If x is smaller, ignore right half
else
r = m - 1;
}
return -1;
}
public static int binaryArtist(Music[] arr, String artist)
{
int l = 0, r = arr.length - 1;
while (r - l >= 1) {
int m = l + (r-l) / 2;
int res = artist.compareTo(arr[m].getArtist());
if (res == 0)
{
return m;
}
if (res > 0)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
return -1;
}
public static void main(String[]args)
{
Music[] arr = new Music[12];
arr[0] = new Music("Montero", 2021, "Lil Nas X");
arr[1] = new Music("Dynamite", 2020, "BTS");
arr[2] = new Music("Bad Guy", 2019, "Billie Eilish");
arr[3] = new Music("Sicko Mode", 2018, "Travis Scott");
arr[4] = new Music("Shape of You", 2017, "Ed Sheeran");
arr[5] = new Music("Heathens", 2016, "Twenty One Pilots");
arr[6] = new Music("See You Again", 2015, "Wiz Khalifa");
arr[7] = new Music("All About That Bass", 2014, "Meghan Trainor");
arr[8] = new Music("Wrecking Ball", 2013, "Miley Cyrus");
arr[9] = new Music("Paradise", 2011, "Coldplay");
arr[10] = new Music("Shake it Off", 2014, "Taylor Swift");
arr[11] = new Music("Savage", 2021, "Aespa");
System.out.println("Original:");
printMusic(arr);
System.out.println("\nBinary searching Sicko Mode: Index " + binaryTitle(arr, "Sicko Mode"));
System.out.println("\nBinary searching Taylor Swift: Index " + binaryArtist(arr, "Taylor Swift"));
}
}
public class Music
{
// instance variables
private int year;
private String title;
private String artist;
// Constructor for objects of class Music
public Music(String t, int y, String a)
{
// initialize instance variables
title = t;
year = y;
artist = a;
}
public String getTitle()
{
return title;
}
public void setTitle(String t)
{
title = t;
}
public String getArtist()
{
return artist;
}
public void setArtist(String a)
{
artist = a;
}
public int getYear()
{
return year;
}
public void setTitle(int y)
{
year = y;
}
public String toString()
{
String str = String.format( "%-25s %4d %-20s ", title, year , artist);
return str;
}
}
for a binary search to work correctly it must be sorted in some way. If you're searching it by year you need to sort it from smallest to largest. if you're searching it by Title, those Titles must be in some alphabetical order, same with the Artist.
Ex:
{1,4,3,2,5} //searching for 4 returns -1 because it's looking between 3 and 5 and only finding 2.
{1,2,3,4,5} //searching for 4 returns 3 because it looks between 3 and 5 and finds 4 at index 3.
Binary search requires a sorted array. If you use an array that's not sorted, binary search is liable to not find what you need. For this type of thing you need a sequential search.
Example:
[0, 3, 7, 8, 12, 56, 2]
//say you have this array, and you're looking for number 2,
//your function will compare 2 to the middle element: 8.
//2 < 8, so it will throw out everything above 8.
[0, 3, 7]
//Obviously 2 is not there. But it was there originally.
//The problem is it was unsorted
I can confirm that you can only do a type of binary search to its corresponding sort. So title binary search can only happen after a title sort.

Forming a pattern of bits from a integer

I need help to design java code for generating bit array for any given integer in following manner:
23 should produce output as 1101011 (min length array)
explaination :
positions are given as 1 -2 4 -8 16 -32 ....
So 1101011 can be evaluated as:
1*1 + 1*-2 + 0*4+ 1*-8 + 0*16 +1*-32 + 1*64 = 23
This is the so-called negabinary representation of numbers (described first by Vittorio Grünwald in 1885). They can be encoded in a fashion very similar to the usual binary representation, just working with -2 instead of 2 as base (Java code inspired by C# code on https://en.wikipedia.org/wiki/Negative_base ):
class EncodeNegaBinary {
public static void main(String[] args) {
int n=0,input=0;
String result="";
final String[] BITS = { "0","1" };
if (args.length != 1) {
System.err.println("Please enter an integer to be converted");
return;
} else {
input = n = Integer.parseInt(args[0]);
}
while (n != 0) {
int r = n%-2;
n /= -2;
if (r == -1) {
r=1;
n++;
}
result = BITS[r] + result;
}
System.out.printf( "%d -> %s\n", input, result);
}
}
Since it is not usual int to binary conversion, at each step we need to consider two cases as at each position there can be only two choices 0 or 1. This is done recursively in the below program:
public class ModifiedIntToBinaryConversion{
public static int calcBinaryString(int reqSum, int currSum, int add, String bs) {
if (reqSum == currSum) { // base condtion 1
System.out.println("The string is \n" + bs);
return currSum;
}
if (add + currSum > reqSum) { // base condtion 2
return 0;
}
int newAdd = add * -2;
// System.out.println("new add is "+ newAdd +" currSum is "+ currSum);
int s1 = calcBinaryString(reqSum, currSum + add, newAdd, bs + "1");
if (s1 == reqSum)
return s1;
int s2 = calcBinaryString(reqSum, currSum, newAdd, bs + "0");
return s2;
}
public static void calcBinaryString(int sum) {
int s1 = calcBinaryString(sum, 0, 1, "");
if(s1 != sum) {
System.out.println("The binary equivalent couldn't be found");
}
}
public static void main(String[] args) {
calcBinaryString(23);
}
}
Now base condition 1 is clear as I am just checking whether required sum and calculated sum are equal.
For base condition 2, I will accept it's result of debugging and a bit of thought as I was getting Stackoverflow errors. Once the calculated sum becomes greater than the required sum and then we take the next -ve number so that it become less than req. sum. But then the next +ve number will be greater than the -ve number we just considered and thus the chances are very less that the calculated sum will ever be equal to req. sum.

integer to word conversion in java using map continue question

This is a probable answer of my question in stack overflow.Integer to word conversion
At first I have started with dictionary. Then I came to know it is obsolete. So now I use Map instead of dictionary. My code is work well for number till Millions. But the approach I take here is a naive approach. The main problem of this code is
First: Huge numbers of variable use
2nd: Redundant code block as per program requirement
3rd: Multiple if else statement
I am thinking about this problems
Solution for 2nd problem: using user define function or macros to eliminate redundant code block
Solution for 3rd problem: Using switch case
My code:
public class IntegerEnglish {
public static void main(String args[]){
Scanner in=new Scanner(System.in);
System.out.println("Enter the integer");
int input_number=in.nextInt();
Map<Integer,String> numbers_converter = new HashMap<Integer,String>();
Map<Integer,String> number_place = new HashMap<Integer,String>();
Map<Integer,String> number_2nd = new HashMap<Integer,String>();
numbers_converter.put(0,"Zero");
numbers_converter.put(1,"One");
numbers_converter.put(2,"Two");
numbers_converter.put(3,"Three");
numbers_converter.put(4,"Four");
numbers_converter.put(5,"Five");
numbers_converter.put(6,"Six");
numbers_converter.put(7,"Seven");
numbers_converter.put(8,"Eight");
numbers_converter.put(9,"Nine");
numbers_converter.put(10,"Ten");
numbers_converter.put(11,"Eleven");
numbers_converter.put(12,"Twelve");
numbers_converter.put(13,"Thirteen");
numbers_converter.put(14,"Fourteen ");
numbers_converter.put(15,"Fifteen");
numbers_converter.put(16,"Sixteen");
numbers_converter.put(17,"Seventeen");
numbers_converter.put(18,"Eighteen");
numbers_converter.put(19,"Nineteen");
number_place.put(3,"Hundred");
number_place.put(4,"Thousand");
number_place.put(7,"Million");
number_place.put(11,"Billion");
number_2nd.put(2,"Twenty");
number_2nd.put(3,"Thirty");
number_2nd.put(4,"Forty");
number_2nd.put(5,"Fifty");
number_2nd.put(6,"Sixty");
number_2nd.put(7,"Seventy");
number_2nd.put(8,"Eighty");
number_2nd.put(9,"Ninty");
if(input_number== 0){
System.out.println("zero");
}
else if(input_number>0 && input_number<19){
System.out.println(numbers_converter.get(input_number));
}
else if(input_number>19 && input_number<100){
int rem=input_number%10;
input_number=input_number/10;
System.out.print(number_2nd.get(input_number));
System.out.print(numbers_converter.get(rem));
}
else if(input_number==100){
System.out.println(number_place.get(3));
}
else if(input_number>100 && input_number<1000){
int reminder=input_number%100;
int r1=reminder%10;
int q1=reminder/10;
int quot=input_number/100;
System.out.print(numbers_converter.get(quot) + "hundred");
if(reminder>0 && reminder<20){
System.out.print(numbers_converter.get(reminder));
}
else{
System.out.println(number_2nd.get(q1) + numbers_converter.get(r1));
}
}
else if(input_number==1000){
System.out.println(number_place.get(4));
}
else if(input_number>1000 && input_number<10000){
int rem=input_number%100;
int rem_two=rem%10;
int quotient =rem/10;
input_number=input_number/100;
int thousand=input_number/10;
int hundred = input_number%10;
System.out.print(numbers_converter.get(thousand) + "thousand" + numbers_converter.get(hundred)+ " hundred");
if(rem >0 && rem<20){
System.out.print(numbers_converter.get(rem));
}
else if(rem >19 && rem <100){
System.out.print(number_2nd.get(quotient) + numbers_converter.get(rem_two));
}
}
else if(input_number>10000 && input_number<1000000000){
//Say number 418,229,356
int third_part=input_number%1000;//hold 356
input_number=input_number/1000;//hold 418,229
int sec_part=input_number%1000;//hold 229
input_number=input_number/1000;// hold 418
int rem_m=third_part%100;//hold 56
int rem_m1=rem_m%10;//hold 6
int rem_q=rem_m/10;// hold 5
int q_m=third_part/100;// hold 3
int sec_part_rem=sec_part%100;// hold 29
int sec_part_rem1=sec_part_rem%10;//9
int sec_part_q=sec_part_rem/10;//hold 2
int sec_q=sec_part/100;// hold 2
int input_q=input_number/100;// hold 4
int input_rem=input_number%100;//hold 18
int input_q_q=input_rem/10;//hold 1
int input_rem1=input_rem%10;// hold 8
System.out.print(numbers_converter.get(input_q) + " hundred ");
if(input_rem>0 && input_rem<20){
System.out.print(numbers_converter.get(input_rem)+ " Million ");
}
else{
System.out.print(number_2nd.get(input_q_q) + " " + numbers_converter.get(input_rem1) + " Million ");
}
System.out.print(numbers_converter.get(sec_q) + " hundred ");
if(sec_part_rem >0 && sec_part_rem<20){
System.out.println(numbers_converter.get(sec_part_rem) + " thousand ");
}
else{
System.out.print(number_2nd.get(sec_part_q) + " " + numbers_converter.get(sec_part_rem1) + " thousand ");
}
System.out.print(numbers_converter.get(q_m) + " hundred ");
if(rem_m>0 && rem_m<20){
System.out.print(numbers_converter.get(rem_m));
}
else{
System.out.print(number_2nd.get(rem_q) + " " + numbers_converter.get(rem_m1));
}
}
}
}
Redundant Code Blocks
int rem=input_number%100;
int rem_two=rem%10;
int quotient =rem/10;
input_number=input_number/100;
int thousand=input_number/10;
int hundred = input_number%10;
This type of code block used almost every where. Taking a number divide it with 100 or 1000 to find out the hundred position then then divide it with 10 to find out the tenth position of the number. Finally using %(modular division) to find out the ones position.
How could I include user define function and switch case to minimize the code block.
Instead of storing the results in variables, use a method call:
int remainder100(int aNumber) {
return aNumber % 100;
}
int remainder10(int aNumber) {
return aNumber % 10;
}
...etc.
System.out.println(numbers_converter.get(remainder100(input_number)));
About 3rd problem: I wouldn't use switch ... case, too many cases.
Instead, take advantage that numbering repeats itself every 3 digits. That means the pattern for thousands and millions is the same (and billions, trillions, etc).
To do that, use a loop like this:
ArrayList<String> partialResult = new ArrayList<String>();
int powersOf1000 = 0;
for (int kiloCounter = input_number; kiloCounter > 0; kiloCounter /= 1000) {
partialResult.add(getThousandsMilionsBillionsEtc(powersOf1000++);
partialResult.add(convertThreeDigits(kiloCounter % 1000));
}
Then you can print out the contents of partialResult in reverse order to get the final number.
I'd suggest you break your single main method down into a couple of classes. And if you haven't already create a few unit tests to allow you to easily test / refactor things. You'll find it quicker than starting the app and reading from stdin.
You'll find it easier to deal with the number as a string. Rather than dividing by 10 all the time you just take the last character of the string. You could have a class that does that bit for you, and a separate one that does the convert.
Here's what I came up with, but I'm sure it can be improved. It has a PoppableNumber class which allows the last character of the initial number to be easily retrieved. And the NumberToString class which has a static convert method to perform the conversion.
An example of a test would be
#Test
public void Convert102356Test() {
assertEquals("one hundred and two thousand three hundred and fifty six", NumberToString.convert(102356));
}
And here's the NumberToString class :
import java.util.HashMap;
import java.util.Map;
public class NumberToString {
// billion is enough for an int, obviously need more for long
private static String[] power3 = new String[] {"", "thousand", "million", "billion"};
private static Map<String,String> numbers_below_twenty = new HashMap<String,String>();
private static Map<String,String> number_tens = new HashMap<String,String>();
static {
numbers_below_twenty.put("0","");
numbers_below_twenty.put("1","one");
numbers_below_twenty.put("2","two");
numbers_below_twenty.put("3","three");
numbers_below_twenty.put("4","four");
numbers_below_twenty.put("5","five");
numbers_below_twenty.put("6","six");
numbers_below_twenty.put("7","seven");
numbers_below_twenty.put("8","eight");
numbers_below_twenty.put("9","nine");
numbers_below_twenty.put("10","ten");
numbers_below_twenty.put("11","eleven");
numbers_below_twenty.put("12","twelve");
numbers_below_twenty.put("13","thirteen");
numbers_below_twenty.put("14","fourteen ");
numbers_below_twenty.put("15","fifteen");
numbers_below_twenty.put("16","sixteen");
numbers_below_twenty.put("17","seventeen");
numbers_below_twenty.put("18","eighteen");
numbers_below_twenty.put("19","nineteen");
number_tens.put(null,"");
number_tens.put("","");
number_tens.put("0","");
number_tens.put("2","twenty");
number_tens.put("3","thirty");
number_tens.put("4","forty");
number_tens.put("5","fifty");
number_tens.put("6","sixty");
number_tens.put("7","seventy");
number_tens.put("8","eighty");
number_tens.put("9","ninty");
}
public static String convert(int value) {
if (value == 0) {
return "zero";
}
PoppableNumber number = new PoppableNumber(value);
String result = "";
int power3Count = 0;
while (number.hasMore()) {
String nextPart = convertUnitTenHundred(number.pop(), number.pop(), number.pop());
nextPart = join(nextPart, " ", power3[power3Count++], true);
result = join(nextPart, " ", result);
}
if (number.isNegative()) {
result = join("minus", " ", result);
}
return result;
}
public static String convertUnitTenHundred(String units, String tens, String hundreds) {
String tens_and_units_part = "";
if (numbers_below_twenty.containsKey(tens+units)) {
tens_and_units_part = numbers_below_twenty.get(tens+units);
}
else {
tens_and_units_part = join(number_tens.get(tens), " ", numbers_below_twenty.get(units));
}
String hundred_part = join(numbers_below_twenty.get(hundreds), " ", "hundred", true);
return join(hundred_part, " and ", tens_and_units_part);
}
public static String join(String part1, String sep, String part2) {
return join(part1, sep, part2, false);
}
public static String join(String part1, String sep, String part2, boolean part1Required) {
if (part1 == null || part1.length() == 0) {
return (part1Required) ? "" : part2;
}
if (part2.length() == 0) {
return part1;
}
return part1 + sep + part2;
}
/**
*
* Convert an int to a string, and allow the last character to be taken off the string using pop() method.
*
* e.g.
* 1432
* Will give 2, then 3, then 4, and finally 1 on subsequent calls to pop().
*
* If there is nothing left, pop() will just return an empty string.
*
*/
static class PoppableNumber {
private int original;
private String number;
private int start;
private int next;
PoppableNumber(int value) {
this.original = value;
this.number = String.valueOf(value);
this.next = number.length();
this.start = (value < 0) ? 1 : 0; // allow for minus sign.
}
boolean isNegative() {
return (original < 0);
}
boolean hasMore() {
return (next > start);
}
String pop() {
return hasMore() ? number.substring(--next, next+1) : "";
}
}
}

Errors : Exception in thread "main" java.lang.Error: Unresolved compilation problems

I was watching a Java tutorial for beginners, and while writing the code I got a few errors:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Syntax error, 'for each' statements are only available if source level is 1.5 or greater
Arrays cannot be resolved
Arrays cannot be resolved
Arrays cannot be resolved
at Animal.main(Animal.java:389)
My code is:
Animal.Java:
import java.util.Scanner;
// A class defines the attributes (fields) and capabilities (methods) of a real world object
public class Animal {
// static means this number is shared by all objects of type Animal
// final means that this value can't be changed
public static final double FAVNUMBER = 1.6180;
// Variables (Fields) start with a letter, underscore or $
// Private fields can only be accessed by other methods in the class
// Strings are objects that hold a series of characters
private String name;
// An integer can hold values from -2 ^ 31 to (2 ^ 31) -1
private int weight;
// Booleans have a value of true or false
private boolean hasOwner = false;
// Bytes can hold the values between -128 to 127
private byte age;
// Longs can hold the values between -2 ^ 63 to (2 ^ 63) - 1
private long uniqueID;
// Chars are unsigned ints that represent UTF-16 codes from 0 to 65,535
private char favoriteChar;
// Doubles are 64 bit IEEE 754 floating points with decimal values
private double speed;
// Floats are 32 bit IEEE 754 floating points with decimal values
private float height;
// Static variables have the same value for every object
// Any variable or function that doesn't make sense for an object to have should be made static
// protected means that this value can only be accessed by other code in the same package
// or by subclasses in other packages
protected static int numberOfAnimals = 0;
// A Scanner object allows you to except user input from the keyboard
static Scanner userInput = new Scanner(System.in);
// Any time an Animal object is created this function called the constructor is called
// to initialize the object
public Animal(){
// Shorthand for numberOfAnimals = numberOfAnimals + 1;
numberOfAnimals++;
int sumOfNumbers = 5 + 1;
System.out.println("5 + 1 = " + sumOfNumbers);
int diffOfNumbers = 5 - 1;
System.out.println("5 - 1 = " + diffOfNumbers);
int multOfNumbers = 5 * 1;
System.out.println("5 * 1 = " + multOfNumbers);
int divOfNumbers = 5 / 1;
System.out.println("5 / 1 = " + divOfNumbers);
int modOfNumbers = 5 % 3;
System.out.println("5 % 3 = " + modOfNumbers);
// print is used to print to the screen, but it doesn't end with a newline \n
System.out.print("Enter the name: \n");
// The if statement performs the actions between the { } if the condition is true
// userInput.hasNextLine() returns true if a String was entered in the keyboard
if(userInput.hasNextLine()){
// this provides you with a way to refer to the object itself
// userInput.nextLine() returns the value that was entered at the keyboard
this.setName(userInput.nextLine());
// hasNextInt, hasNextFloat, hasNextDouble, hasNextBoolean, hasNextByte,
// hasNextLong, nextInt, nextDouble, nextFloat, nextBoolean, etc.
}
this.setFavoriteChar();
this.setUniqueID();
}
// It is good to use getter and setter methods so that you can protect your data
// In Eclipse Right Click -> Source -> Generate Getter and Setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public boolean isHasOwner() {
return hasOwner;
}
public void setHasOwner(boolean hasOwner) {
this.hasOwner = hasOwner;
}
public byte getAge() {
return age;
}
public void setAge(byte age) {
this.age = age;
}
public long getUniqueID() {
return uniqueID;
}
// Method overloading allows you to accept different input with the same method name
public void setUniqueID(long uniqueID) {
this.uniqueID = uniqueID;
System.out.println("Unique ID set to: " + this.uniqueID);
}
public void setUniqueID() {
long minNumber = 1;
long maxNumber = 1000000;
// Generates a random number between 1 and 1000000
this.uniqueID = minNumber + (long)(Math.random() * ((maxNumber - minNumber) + 1));
// You can cast from one primitive value into another by putting what you want between ( )
// (byte) (short) (long) (double)
// (float), (boolean) & (char) don't work.
// (char) stays as a number instead of a character
// You convert from a primitive to a string like this
String stringNumber = Long.toString(maxNumber);
// Byte.toString(bigByte); Short.toString(bigShort); Integer.toString(bigInt);
// Float.toString(bigFloat); Double.toString(bigDouble); Boolean.toString(trueOrFalse);
// You convert from a String to a primitive like this
int numberString = Integer.parseInt(stringNumber);
// parseShort, parseLong, parseByte, parseFloat, parseDouble, parseBoolean
System.out.println("Unique ID set to: " + this.uniqueID);
}
public char getFavoriteChar() {
return favoriteChar;
}
public void setFavoriteChar(char favoriteChar) {
this.favoriteChar = favoriteChar;
}
public void setFavoriteChar() {
int randomNumber = (int) (Math.random() * 126) + 1;
this.favoriteChar = (char) randomNumber;
// if then else statement
// > < == != >= <=
if(randomNumber == 32){
System.out.println("Favorite character set to: Space");
} else if(randomNumber == 10){
System.out.println("Favorite character set to: New Line");
} else {
System.out.println("Favorite character set to: " + this.favoriteChar);
}
// Logical operators
// ! : Converts the boolean value to its right to its opposite form ie. true to false
// & : Returns true if boolean value on the right and left are both true (Always evaluates both boolean values)
// && : Returns true if boolean value on the right and left are both true (Stops evaluating after first false)
// | : Returns true if either boolean value on the right or left are true (Always evaluates both boolean values)
// || : Returns true if either boolean value on the right or left are true (Stops evaluating after first true)
// ^ : Returns true if there is 1 true and 1 false boolean value on the right or left
if((randomNumber > 97) && (randomNumber < 122)){
System.out.println("Favorite character is a lowercase letter");
}
if(((randomNumber > 97) && (randomNumber < 122)) || ((randomNumber > 64) && (randomNumber < 91))){
System.out.println("Favorite character is a letter");
}
if(!false){
System.out.println("I turned false to " + !false);
}
// The ternary operator assigns one or another value based on a condition
int whichIsBigger = (50 > randomNumber) ? 50 : randomNumber;
System.out.println("The biggest number is " + whichIsBigger);
// The switch statement is great for when you have a limited number of values
// and the values are int, byte, or char unless you have Java 7 which allows Strings
switch(randomNumber){
case 8 :
System.out.println("Favorite character set to: Backspace");
break;
case 9 :
System.out.println("Favorite character set to: Horizontal Tab");
break;
case 10 :
case 11 :
case 12 :
System.out.println("Favorite character set to: Something else weird");
break;
default :
System.out.println("Favorite character set to: " + this.favoriteChar);
break;
}
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
protected static int getNumberOfAnimals() {
return numberOfAnimals;
}
// Since numberOfAnimals is Static you must set the value using the class name
public void setNumberOfAnimals(int numberOfAnimals) {
Animal.numberOfAnimals = numberOfAnimals;
}
protected static void countTo(int startingNumber){
for(int i = startingNumber; i <= 100; i++){
// continue is used to skip 1 iteration of the loop
if(i == 90) continue;
System.out.println(i);
}
}
protected static String printNumbers(int maxNumbers){
int i = 1;
while(i < (maxNumbers / 2)){
System.out.println(i);
i++;
// This isn't needed, but if you want to jump out of a loop use break
if(i == (maxNumbers/2)) break;
}
Animal.countTo(maxNumbers/2);
// You can return a value like this
return "End of printNumbers()";
}
protected static void guessMyNumber(){
int number;
// Do while loops are used when you want to execute the code in the braces at least once
do {
System.out.println("Guess my number up to 100");
// If what they entered isn't a number send a warning
while(!userInput.hasNextInt()){
String numberEntered = userInput.next();
System.out.printf("%s is not a number\n", numberEntered);
}
number = userInput.nextInt();
}while(number != 50);
System.out.println("Yes the number was 50");
}
// This will be used to demonstrate polymorphism
public String makeSound(){
return "Grrrr";
}
// With polymorphism we can refer to any Animal and yet use overridden methods
// in the specific animal type
public static void speakAnimal(Animal randAnimal){
System.out.println("Animal says " + randAnimal.makeSound());
}
// public allows other classes to use this method
// static means that only a class can call for this to execute
// void means it doesn't return a value when it finishes executing
// This method can except Strings that can be stored in the String array args when it is executed
public static void main(String[] args){
Animal theDog = new Animal();
System.out.println("The animal is named " + theDog.getName());
System.out.println(Animal.printNumbers(100));
Animal.countTo(100);
Animal.guessMyNumber();
// An array is a fixed series of boxes that contain multiple values of the same data type
// How you create arrays
// int[] favoriteNumbers;
// favoriteNumbers = new int[20];
int[] favoriteNumbers = new int[20];
favoriteNumbers[0] = 100;
String[] stringArray = {"Random", "Words", "Here"};
// for(dataType[] varForRow : arrayName)
for(String word : stringArray)
{
System.out.println(word);
}
// This is a multidimensional array
String[][][] arrayName = { { { "000" }, { "100" }, { "200" }, { "300" } },
{ { "010" }, { "110" }, { "210" }, { "310" } },
{ { "020" }, { "120" }, { "220" }, { "320" } }};
for(int i = 0; i < arrayName.length; i++)
{
for(int j = 0; j < arrayName[i].length; j++)
{
for(int k = 0; k < arrayName[i][j].length; k++)
{
System.out.print("| " + arrayName[i][j][k] + " ");
}
}
System.out.println("|");
}
// You can copy an array (stringToCopy, indexes to copy)
String[] cloneOfArray = Arrays.copyOf(stringArray, 3);
// You can print out the whole array
System.out.println(Arrays.toString(cloneOfArray));
// Returns the index or a negative number
System.out.println(Arrays.binarySearch(cloneOfArray, "Random"));
}
}
Dog.Java
public class Dog extends Animal{
public Dog() {
}
// You can override Animal methods
public String makeSound(){
return "Woof";
}
public static void main(String[] args) {
Dog fido = new Dog();
fido.setName("Fido");
System.out.println(fido.getName());
}
}
Cat.java
public static void main(String[] args) {
Animal fido = new Dog();
Animal fluffy = new Cat();
// We can have an array of Animals that contain more specific subclasses
// Any overridden methods are used instead because of polymorphism
Animal[] theAnimals = new Animal[10];
theAnimals[0] = fido;
theAnimals[1] = fluffy;
System.out.println("Fido says " + theAnimals[0].makeSound());
System.out.println("Fluffy says " + theAnimals[1].makeSound());
// We can also pass subclasses of Animal and they just work
speakAnimal(fluffy);
}
}
I saw a few other answers here and read that I had to add import java.util.Scanner;
Which i already had..
Please tell me the problem in the code...
Thanks!
The line for(String word : stringArray) is a so called "for each" loop - a convenience added in Java 1.5.
But it seems your compiler is configued below 1.5, so it does not support the for each loop.
So you can either:
change the compiler level
change the loop to be 1.4 compatible to
String[] stringArray = {"Random", "Words", "Here"};
String word;
for(int i = 0; i < stringArray.length; i++)
{
word = stringArray[i];
System.out.println(word);
}
(in fact this is what the compiler would do for you...)
The issue is clear from the error message -
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Syntax error, 'for each' statements are only available if source level is 1.5 or greater
You need java 1.5 or higher, You can download latest Java JDKs's from here
Or you can do as ultimate said, and make your loops compatible for lower versions of java (though I would suggest upgrading to above 1.5 , 1.5 is like very very old version anyway).

Sort algorithm problems on java comparable

I want to do a specific sort. I am using java's comparable interface which means the return of my compare method must return -1 +1 or 0 depending on the equality of the two compared, then I am sorting using Collections. My trouble comes from how I wish to compare.
I have a key that is made up of either of the following
[keyName]
[siteName].[keyName]
[siteName].[pageName].[keyName]
so as an example "mysite.alampshade.color"
the tricky part is the sites must be sorted first, followed by keyname, followed by pageName. but firstly by the keynames, then site name, in the order of the number of sections to the property. Sorry. its a little complicated, an example may help. here is the order they must be:
alpha
beta
charlie
sitea.alpha
sitea.charlie
sitea.pagea.beta
sitea.pageb.beta
sitea.pagea.charlie
siteb.alpha
siteb.delta
siteb.pagef.alpha
siteb.pageb.echo
siteb.pageb.golf
siteb.pagea.hotel
siteb.pageb.hotel
siteb.pagec.hotel
I have tried many different ways and have thrown away code a few times but still cant get it perfect. some pseudocode would be of great help if not some java.
EDIT:
to add another possibly simplier to understand example
the following is sorted how I need it
a
b
c
z
a.b
a.c
a.d
a.z
a.b.a
a.c.a
a.b.b
a.b.c
a.c.c
a.a.d
b.a
b.b
b.z
b.a.a
b.b.a
b.a.b
c.c.f
Another option, making it recursive you avoid the problem if there is ever more entries.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class SortTest {
public static void main(String[] args) {
String[] test = new String[]{
"a",
"b",
"b.a",
"b.a.a",
"a.a.a",
"a.b.a",
"a.a",
"a.b",
"b.a.b",
"b.b.a"
};
Arrays.sort(test, new Comparator<String>() {
int compareComplexList(List<String> a, List<String> b, List<int[]> positions, int order ) {
int minimum = a.size() < b.size() ? a.size() - 1 : b.size() - 1;
if (a.get(positions.get(minimum)[order]).compareTo(b.get(positions.get(minimum)[order])) != 0)
return a.get(positions.get(minimum)[order]).compareTo(b.get(positions.get(minimum)[order]));
else if (order < minimum - 1) return compareComplexList(a,b, positions, ++order);
else return Double.compare(a.size(),b.size());
}
public int compare(String a, String b) {
List<String> partsA = Arrays.asList(a.split("\\."));
List<String> partsB = Arrays.asList(b.split("\\."));
List<int[]> orders = new ArrayList<int[]>();
orders.add(new int[] {0});
orders.add(new int[] {0,1});
orders.add(new int[] {0,2,1});
return compareComplexList(partsA, partsB, orders,0);
}
});
System.out.println("Sorted: "+Arrays.toString(test));
}
}
Should be good now.
public int compare(String a, String b) {
String[] partsA = a.split("\\.");
String[] partsB = b.split("\\.");
// If first term is different, we exit.
if (partsA[0].compareTo(partsB[0]) != 0) return partsA[0].compareTo(partsB[0]);
// Else, first term is identical.
else {
// Same number of parts
if (partsA.length == partsB.length) {
// 2 parts, we compare the 2nd part.
if (partsA.length == 2) {
return partsA[1].compareTo(partsB[1]);
// 3 parts, we compare the 3rd part first, then the 2nd part
} else {
if (partsA[2].compareTo(partsB[2]) != 0) return partsA[2].compareTo(partsB[2]);
return partsA[1].compareTo(partsB[1]);
}
// Different number of parts
} else {
// If A has only 1 part, it's first
if (partsA.length == 1) return -1;
// If B has only 1 part, it's first
if (partsB.length == 1) return 1;
// Case 2 vs 3 parts, we compare the 3rd part with the 2nd part of the other. If it's equal, the shorter is first.
if (partsA.length == 3) {
if (partsA[2].compareTo(partsB[1]) != 0) return partsA[2].compareTo(partsB[1]);
else return 1;
} else {
if (partsA[1].compareTo(partsB[2]) != 0) return partsA[1].compareTo(partsB[2]);
else return -1;
}
}
}
}
My other answer started getting too gnarly. Here's a better, more natural solution:
public class StrangeComparator {
private static class Entry implements Comparable<Entry> {
// What to split with.
static final String dot = Pattern.quote(".");
// The parts.
final String key;
final String page;
final String site;
public Entry(String s) {
String [] parts = s.split(dot);
switch (parts.length) {
case 1:
key = parts[0];
page = "";
site = "";
break;
case 2:
key = parts[1];
page = "";
site = parts[0];
break;
case 3:
key = parts[2];
page = parts[1];
site = parts[0];
break;
default:
throw new IllegalArgumentException("There must be at least one part to an entry.");
}
}
#Override
public int compareTo(Entry t) {
int diff = site.compareTo(t.site);
if ( diff == 0 ) {
diff = page.compareTo(t.page);
}
if ( diff == 0 ) {
diff = key.compareTo(t.key);
}
return diff;
}
#Override
public String toString () {
return (site.length() > 0 ? site + "." : "")
+ (page.length() > 0 ? page + "." : "")
+ key;
}
}
public void test() {
String[] test = new String[]{
"alpha",
"beta",
"charlie",
"zeta", // Added to demonstrate correctness.
"sitea.alpha",
"sitea.charlie",
"sitea.pagea.beta",
"sitea.pageb.beta",
"sitea.pagea.charlie",
"siteb.alpha",
"siteb.delta",
"siteb.pagef.alpha",
"siteb.pageb.echo",
"siteb.pageb.golf",
"siteb.pagea.hotel",
"siteb.pageb.hotel",
"siteb.pagec.hotel"
};
Arrays.sort(test);
System.out.println("Normal sort: " + Separator.separate("\n", "\n", test));
Entry[] entries = new Entry[test.length];
for ( int i = 0; i < test.length; i++ ) {
entries[i] = new Entry(test[i]);
}
Arrays.sort(entries);
System.out.println("Special sort: " + Separator.separate("\n", "\n", entries));
}
public static void main(String args[]) {
new StrangeComparator().test();
}
}
Output order is:
alpha
beta
charlie
zeta
sitea.alpha
sitea.charlie
sitea.pagea.beta
sitea.pagea.charlie
sitea.pageb.beta
siteb.alpha
siteb.delta
siteb.pagea.hotel
siteb.pageb.echo
siteb.pageb.golf
siteb.pageb.hotel
siteb.pagec.hotel
siteb.pagef.alpha
Which kinda does what you say but doesn't match your example.
Here's an alternative - if a component is found to contain less that 3 parts then parts are added at the start to take up the slack. It then uses a sort order array to define which columns should be compared next:
public void test() {
String[] test = new String[]{
"alpha",
"beta",
"charlie",
"zeta", // Added to demonstrate correctness.
"sitea.alpha",
"sitea.charlie",
"sitea.pagea.beta",
"sitea.pageb.beta",
"sitea.pagea.charlie",
"siteb.alpha",
"siteb.delta",
"siteb.pagef.alpha",
"siteb.pageb.echo",
"siteb.pageb.golf",
"siteb.pagea.hotel",
"siteb.pageb.hotel",
"siteb.pagec.hotel"
};
Arrays.sort(test);
System.out.println("Normal sort: "+Arrays.toString(test));
Arrays.sort(test, new Comparator<String>() {
// How many columns to pad to.
final int padTo = 3;
// What to pad with.
final String padWith = "";
// What order to compare the resultant columns in.
final int[] order = {0, 2, 1};
#Override
public int compare(String s1, String s2) {
String[] s1parts = padArray(s1.split(Pattern.quote(".")), padTo, padWith);
String[] s2parts = padArray(s2.split(Pattern.quote(".")), padTo, padWith);
int diff = 0;
for ( int i = 0; diff == 0 && i < order.length; i++ ) {
diff = s1parts[order[i]].compareTo(s2parts[order[i]]);
}
return diff;
}
String [] padArray(String[] array, int padTo, String padWith) {
String [] padded = new String[padTo];
for ( int i = 0; i < padded.length; i++ ) {
padded[padded.length - i - 1] = i < array.length ? array[i]: padWith;
}
return padded;
}
});
System.out.println("Special sort: "+Arrays.toString(test));
}
prints (more or less):
Normal sort: [alpha,
beta,
charlie,
sitea.alpha,
sitea.charlie,
sitea.pagea.beta,
sitea.pagea.charlie,
sitea.pageb.beta,
siteb.alpha,
siteb.delta,
siteb.pagea.hotel,
siteb.pageb.echo,
siteb.pageb.golf,
siteb.pageb.hotel,
siteb.pagec.hotel,
siteb.pagef.alpha,
zeta]
Special sort: [alpha,
beta,
charlie,
sitea.alpha,
sitea.charlie,
siteb.alpha,
siteb.delta,
zeta,
siteb.pagef.alpha,
sitea.pagea.beta,
sitea.pageb.beta,
sitea.pagea.charlie,
siteb.pageb.echo,
siteb.pageb.golf,
siteb.pagea.hotel,
siteb.pageb.hotel,
siteb.pagec.hotel]
There does seem to be some ambiguity in your requirements but this code is structured so you can, with trivial tweaks, achieve most interpretations of your comparison quite simply.

Categories