darkangel1283
darkangel1283

Reputation: 81

Formatting a java dice rolling program?

I'm making a java program where a random dice face is selected and displayed. Ultimately, I want to show five dice side by side in the console. I have made some strings that are ascii representations of the 6 dice faces.

Here is an example of one:

+-------+
| o   o |
|   o   |
| o   o |
+-------+

My question is: how would I display five of these ascii dice side by side in the console? I want the console output to look something like this:

+-------+   +-------+   +-------+   +-------+   +-------+
| o   o |   | o   o |   | o   o |   | o   o |   | o   o |
|   o   |   |   o   |   |   o   |   |   o   |   |   o   |
| o   o |   | o   o |   | o   o |   | o   o |   | o   o |
+-------+   +-------+   +-------+   +-------+   +-------+

Here is my code:

public class Die
{
    private final static int LOWEST_DIE_VALUE = 0;
    private final static int HIGHEST_DIE_VALUE = 6;

    private String die;

    public Die()
    {
        String[] dieFaces = {"+-------+\r\n|       |\r\n|   o   |\r\n|       |\r\n+-------+", 
                             "+-------+\r\n| o     |\r\n|       |\r\n|     o |\r\n+-------+",
                             "+-------+\r\n| o     |\r\n|   o   |\r\n|     o |\r\n+-------+",
                             "+-------+\r\n| o   o |\r\n|       |\r\n| o   o |\r\n+-------+",
                             "+-------+\r\n| o   o |\r\n|   o   |\r\n| o   o |\r\n+-------+",
                             "+-------+\r\n| o   o |\r\n| o   o |\r\n| o   o |\r\n+-------+"};

        die = dieFaces[((int)(Math.random() * 100) % HIGHEST_DIE_VALUE + LOWEST_DIE_VALUE)];        
    }

    public String getDie()
    {
        return die;
    }
}

public class FiveDice
{
    public static void main(String[] args)
    {
        Die die1 = new Die();
        System.out.println(die1.getDie());
    }
}

Upvotes: 8

Views: 2874

Answers (4)

Marco13
Marco13

Reputation: 54639

For cases like this, I tend to create somewhat generic solutions. This may be "overengineering" for the particular case of the question. But I think that the result may be a useful building block for application cases.

  • There is a set of strings that is supposed to be formatted "nicely"
  • The number of strings is not known.
  • The strings can contain multiple lines (i.e. they may contain line breaks, \n or \r\n).
  • The number of line breaks is not known.
  • The line lengths are not known.

One approach here would be to break the strings into multiple lines, and consider them as "blocks" that should be concatenated horizontally.

The format method in the example below does exactly this:

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MultiLineFormatting
{
    public static void main(String[] args)
    {
        showDice();
        showOtherStrings();
    }

    private static void showDice()
    {
        String s1 = 
            "+-------+" + "\n" +
            "|       |" + "\n" +
            "|   o   |" + "\n" +
            "|       |" + "\n" +
            "+-------+";
        String s4 = 
            "+-------+" + "\n" +
            "| o   o |" + "\n" +
            "|       |" + "\n" +
            "| o   o |" + "\n" +
            "+-------+";
        String s5 = 
            "+-------+" + "\n" +
            "| o   o |" + "\n" +
            "|   o   |" + "\n" +
            "| o   o |" + "\n" +
            "+-------+";

        System.out.println(format(Arrays.asList(s1, s4, s5, s4), "  "));
    }

    private static void showOtherStrings()
    {
        String sA = 
            "AAA" + "\n" +
            "AAAAAAAA" + "\n" +
            "AAAAA";
        String sB = 
            "BBBBBBBBBBBBBB" + "\n" +
            "BBBBB" + "\n" +
            "BBBBBBBBBB" + "\n" +
            "BBBBBBBBBBBBBBBBB" + "\n" +
            "BBBBB";
        String sC = 
            "CCC" + "\n" +
            "" + "\n" +
            "CCCCCCC" + "\n" +
            "CCCC";

        System.out.println(format(Arrays.asList(sA, sB, sC, sA), "  "));
    }

    private static String format(Collection<String> strings, String padding)
    {
        StringBuilder sb = new StringBuilder();

        // Convert the strings to lists of strings, breaking them at newlines
        List<List<String>> lineLists = strings.stream()
            .map(s -> s.split("\\R"))
            .map(Arrays::asList)
            .collect(Collectors.toList());

        // Compute the maximum number of lines of any block
        int maxNumLines = lineLists.stream()
            .mapToInt(Collection::size)
            .max().getAsInt();

        // Compute a map from each block index to the maximum length of a line
        Map<Integer, Integer> maxLineLengths = 
            new LinkedHashMap<Integer, Integer>();
        for (int i = 0; i < lineLists.size(); i++)
        {
            List<String> lineList = lineLists.get(i);
            int maxLineLength = lineList.stream()
                .mapToInt(String::length)
                .max().getAsInt();
            maxLineLengths.put(i, maxLineLength);
        }

        // Assemble the resulting lines
        for (int i = 0; i < maxNumLines; i++)
        {
            // Concatenate the i'th line of each block
            for (int j = 0; j < lineLists.size(); j++)
            {
                List<String> lineList = lineLists.get(j);
                int maxLineLength = maxLineLengths.get(j);
                String format = "%-" + maxLineLength + "s";
                if (i < lineList.size())
                {
                    String line = lineList.get(i);
                    sb.append(String.format(format, line));
                }
                else
                {
                    sb.append(String.format(format, ""));
                }
                sb.append(padding);
            }
            sb.append("\n");
        }
        return sb.toString();
    }
}

For the example of the dice, the output is as desired:

+-------+  +-------+  +-------+  +-------+  
|       |  | o   o |  | o   o |  | o   o |  
|   o   |  |       |  |   o   |  |       |  
|       |  | o   o |  | o   o |  | o   o |  
+-------+  +-------+  +-------+  +-------+  

As an additional test, there is an example where other strings are formatted, with different numbers of lines and line lengths:

AAA       BBBBBBBBBBBBBB     CCC      AAA       
AAAAAAAA  BBBBB                       AAAAAAAA  
AAAAA     BBBBBBBBBB         CCCCCCC  AAAAA     
          BBBBBBBBBBBBBBBBB  CCCC               
          BBBBB                                 

Upvotes: 1

IEE1394
IEE1394

Reputation: 1261

I did not add comments to reduce the length. If you have questions, ask :-)

The idea is to have a boolean matrix(3x3) for the "eye-fields." DiceSide takes care of setting the fields according to the number to be displayed. Two fields are not considered (top middle, bottom middle) cause they are always empty.

The createString method prints the dice as rows (each diceside has 5 rows (headline, three eye rows and bottom)).

import java.util.Arrays;
import java.util.List;

public class AsciiDice {
    private static class DiceSide {

        public DiceSide(final int nr) {
            setNr(nr);
        }

        public void setNr(final int nr) {
            if (nr <= 0 || nr > 6) return;

            fields[0][0] = nr == 2 || nr == 3 || nr == 4 || nr == 5 || nr == 6;
            fields[0][2] = nr == 4 || nr == 5 || nr == 6;
            fields[1][0] = nr == 6;
            fields[1][1] = nr == 1 || nr == 3 || nr == 5;
            fields[1][2] = nr == 6;
            fields[2][0] = nr == 4 || nr == 5 || nr == 6;
            fields[2][2] = nr == 2 || nr == 3 || nr == 4 || nr == 5 || nr == 6;
        }

        final boolean[][] fields = new boolean[3][3];
    }

    public static void main(final String[] args) {
        new AsciiDice();
    }

    public AsciiDice() {
        final List<DiceSide> diceSides = Arrays.asList(new DiceSide(5), new DiceSide(4), new DiceSide(3));
        final String str = createString(diceSides);
        System.out.println(str);
    }

    private String createString(final List<DiceSide> diceSides) {
        final StringBuilder sb = new StringBuilder();

        for (int i = 0; i < 5; i++) {
            for (final DiceSide side : diceSides) {
                if (i == 0 || i == 4) {
                    sb.append("+-------+");
                } else {
                    sb.append("| ");
                    for (int c = 0; c < 3; c++) {
                        final boolean set = side.fields[i - 1][c];
                        if (set) {
                            sb.append("o ");
                        } else {
                            sb.append("  ");
                        }
                    }
                    sb.append("|");
                }

                sb.append("\t");
            }
            sb.append("\n");
        }
        return sb.toString();
    }

}

Upvotes: 0

Charles Spencer
Charles Spencer

Reputation: 657

Here's what I came up with. There's a Die class that lets you create an Iterator through the different lines, and then we get a list of these Iterator's and print out the first line for each of the dice, then the second line, etc.

Here is the Die class:

  class Die implements Iterable<String> {
    private static final String[][] lines = {
      {
        "+-------+",
        "|       |",
        "|   o   |",
        "|       |",
        "+-------+",
      },
      {
        "+-------+",
        "| o     |",
        "|       |",
        "|     o |",
        "+-------+",
      },
      {
        "+-------+",
        "| o     |",
        "|   o   |",
        "|     o |",
        "+-------+",
      },
      {
        "+-------+",
        "| o   o |",
        "|       |",
        "| o   o |",
        "+-------+",
      },
      {
        "+-------+",
        "| o   o |",
        "|   o   |",
        "| o   o |",
        "+-------+",
      },
      {
        "+-------+",
        "| o   o |",
        "| o   o |",
        "| o   o |",
        "+-------+",
      }
    };

    private int value;

    public Die(int value) {
      if (value < 0 || value >= 7) {
        throw new IllegalArgumentException("Illegal die value");
      }
      this.value = value;
    }

    public Iterator<String> iterator() {
      return Arrays.asList(lines[value]).iterator();
    }
  }

Here is the class with the main method that collects the lines in a StringBuilder and prints them:

public class Dice {
  public static void main(String[] args) {
    Die die1 = new Die(0);
    Die die2 = new Die(1);
    Die die3 = new Die(2);
    Die die4 = new Die(3);
    Die die5 = new Die(4);

    List<Iterator<String>> dice = Arrays.asList(
        die1.iterator(),
        die2.iterator(),
        die3.iterator(),
        die4.iterator(),
        die5.iterator());
    StringBuilder sb = new StringBuilder();

    outer:
    while (true) {
      for (Iterator<String> iter : dice) {
        if (!iter.hasNext()) {
          break outer;
        }
      }

      for (Iterator<String> iter : dice) {
        sb.append(iter.next()).append("   ");
      }
      sb.append("\r\n");
    }

    System.out.println(sb);
  }
}

The output of this code is the following:

+-------+   +-------+   +-------+   +-------+   +-------+   
|       |   | o     |   | o     |   | o   o |   | o   o |   
|   o   |   |       |   |   o   |   |       |   |   o   |   
|       |   |     o |   |     o |   | o   o |   | o   o |   
+-------+   +-------+   +-------+   +-------+   +-------+  

Upvotes: 2

Brian
Brian

Reputation: 17309

This sounds a bit like homework (apologies if I'm wrong), so I'll get you started in the right direction instead of providing a full solution.

Consider the six possible dice:

+-------+   +-------+   +-------+   +-------+   +-------+   +-------+
|       |   | o     |   | o     |   | o   o |   | o   o |   | o   o |
|   o   |   |       |   |   o   |   |       |   |   o   |   | o   o |
|       |   |     o |   |     o |   | o   o |   | o   o |   | o   o |
+-------+   +-------+   +-------+   +-------+   +-------+   +-------+

How unique are the pieces of these, really? You can break it down into 6 strings:

private static final String xxxxxxxxx = "+-------+";
private static final String x_______x = "|       |";
private static final String x_o_____x = "| o     |";
private static final String x___o___x = "|   o   |";
private static final String x_____o_x = "|     o |";
private static final String x_o___o_x = "| o   o |";

Every die face can be represented by combining these strings together along with newlines, e.g.:

public String[] getLines(int face) {
    switch (face) {
        case 1:
            return new String[] {
                    xxxxxxxxx,
                    x_______x,
                    x___o___x,
                    x_______x,
                    xxxxxxxxx
            };
        case 2:
            ...
        case 3:
            ...
        // etc.
}

If you wanted to render them side-by-side, think about how could you arrange a couple of arrays such that you're iterating over each die to print one line at a time.

Upvotes: 14

Related Questions