package io.rudefox.burrow; import com.bjdweck.bitcoin.mnemonic.Entropy; import com.diogonunes.jcolor.AnsiFormat; import java.util.Scanner; import java.util.regex.Pattern; import static com.diogonunes.jcolor.Attribute.*; public class Dice8EntropyGenerator { public static final AnsiFormat GREEN_STYLE = new AnsiFormat(BLACK_TEXT(), BACK_COLOR(84), BOLD()); public static final AnsiFormat YELLOW_STYLE = new AnsiFormat(BLACK_TEXT(), BACK_COLOR(228), BOLD()); public static final AnsiFormat RED_STYLE = new AnsiFormat(BLACK_TEXT(), BACK_COLOR(213), BOLD()); public static final int DICE_PER_ROLL = 11; private final int targetBitsOfEntropy; private final Scanner inputScanner; private final boolean ansiColorOutput; private Entropy entropy = new Entropy(); public Dice8EntropyGenerator(int targetBitsOfEntropy, Scanner inputScanner, boolean ansiColorOutput) { this.targetBitsOfEntropy = targetBitsOfEntropy; this.inputScanner = inputScanner; this.ansiColorOutput = ansiColorOutput; } Entropy generate() { entropy = new Entropy(); for (int rollSetCount = 0; entropy.getBitLength() < targetBitsOfEntropy; rollSetCount++) doDiceRoll(rollSetCount); return entropy.truncate(targetBitsOfEntropy).appendChecksum(); } private void doDiceRoll(int currentRollSet) { String eventString = scanNextRollSetString(); StringBuilder rollValuesLine = new StringBuilder("|"); for (int rollNumber = 0; rollNumber < DICE_PER_ROLL; rollNumber++) { int rollValue = Integer.parseInt(eventString.charAt(rollNumber) + ""); String formatString = rollNumber == 3 || rollNumber == 7 ? " %d |" : " %d |"; rollValuesLine.append(String.format(formatString, rollValue)); entropy = entropy.appendBits(rollValue - 1, 3); } System.out.printf("%n%s%n%s%n%s%n%n", rollValuesLine, getBitsLine(), getSeedWordsLine(currentRollSet) ); } private String scanNextRollSetString() { String rollSetString = ""; Pattern rollSetPattern = Pattern.compile(String.format("[1-8]{%d}", DICE_PER_ROLL)); while (!rollSetPattern.matcher(rollSetString).matches()) { System.out.print("Input 11 x 8-sided dice rolls [1-8]: "); rollSetString = inputScanner.next(); } return rollSetString; } private StringBuilder getBitsLine() { String entropyBitString = entropy.toString(); String currentRollSetBitString = entropyBitString.substring(entropyBitString.length() - (DICE_PER_ROLL * 3)); StringBuilder rollSetBitsLine = new StringBuilder("|"); for (int rollSetBitIndex = 0; rollSetBitIndex < currentRollSetBitString.length(); rollSetBitIndex++) { int entropyBitIndex = entropyBitString.length() - currentRollSetBitString.length() + rollSetBitIndex; if (entropyBitIndex < targetBitsOfEntropy) rollSetBitsLine.append(styleBit(rollSetBitIndex, "" + currentRollSetBitString.charAt(rollSetBitIndex))); else rollSetBitsLine.append("-"); if (rollSetBitIndex == 32) rollSetBitsLine.append("|"); else if (rollSetBitIndex % 3 == 2) rollSetBitsLine.append(styleBit(rollSetBitIndex, " ")); else if (rollSetBitIndex % 11 == 10) rollSetBitsLine.append(" | "); } return rollSetBitsLine; } private String styleBit(int rollSetBitIndex, String s) { if (!ansiColorOutput) return s; return getBitFormat(rollSetBitIndex).format(s); } private AnsiFormat getBitFormat(int rollSetBitIndex) { int wordBitIndex = rollSetBitIndex % 11; if (wordBitIndex == 0) return GREEN_STYLE; if (wordBitIndex <= 6) return YELLOW_STYLE; return RED_STYLE; } private String getSeedWordsLine(int rollSet) { int baseIndex = rollSet * 3; return String.format( "|%-15s|%-17s|%-15s|", getFormattedSeedWord(baseIndex), getFormattedSeedWord(baseIndex + 1), getFormattedSeedWord(baseIndex + 2)); } private String getFormattedSeedWord(int index) { int lastWordIndex = (3 * targetBitsOfEntropy / 32) - 1; String seedWord = index != lastWordIndex ? entropy.getWord(index) : "CHECKWORD"; return String.format(" %2d. %s", index + 1, seedWord); } }