I'm build a blockchain app.
When I run tests in main, no matter what I do, no matter how much time I give it, when I log different things out, I'm unable to get 4 leading zeroes and so complete a difficulty level of 4. I see the log of the binary hashes and many times they have repeating elements, 1111 for instance, but never 0000 until my time is hit and the difficulty decreases to three. I have no idea why.
I borrowed the hash algorithm from an online source and I checked its output against an online hasher and it checked out.
I know with each level of difficulty it increases exponentially but 2^4 is still only 16 and I see other repeating numbers (1111, 1010, any combination except 0000). Is there any reason why this might be the case?
I wanted to provide an abundance of code rather than a shortage. Logically it makes no sense why randomly if all numbers were equally possible, it woudln't turn up 0000* (e.g. 0000101011at some point). therefore Four zeros must not be possible, but why? I waited 100 seconds mutliple times and saw other numbers repeat themselves. I saw it hit at exactly 4 or 3 or 2 seconds each time on the dot when difficulty went to three. When I start at difficulty 5 (genesis block) it will never solve- I'm sure even if I left it running overnight. So what could be going on?
package privblock.gerald.ryan;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Arrays;
import java.util.Date; // gets time in ms.
import privblock.gerald.ryan.util.CryptoHash;
/**
*
* #author Gerald Ryan Block Class of blockchain app
*
* Description: The block hash is the result of the timestamp, the
* last_hash, the data, the difficulty and the nonce
*
*/
public class Block {
long timestamp;
String lastHash;
String hash;
String[] data;
int difficulty;
int nonce;
// Millisecond basis
;
static long MILLISECONDS = 1;
static long SECONDS = 1000 * MILLISECONDS;
static long MINE_RATE = 2 * SECONDS;
/**
* A block is a unit of storage for a blockchain that supports a cryptocurrency.
*
* #param timestamp
* #param lastHash
* #param hash
* #param data
* #param difficulty
* #param nonce
*/
public Block(long timestamp, String lastHash, String hash, String[] data, int difficulty, int nonce) {
super();
this.timestamp = timestamp;
this.lastHash = lastHash;
this.hash = hash;
this.data = data;
this.difficulty = difficulty;
this.nonce = nonce;
}
public String toString() {
return "\n-----------BLOCK--------\ntimestamp: " + this.timestamp + "\nlastHash: " + this.lastHash + "\nhash: "
+ this.hash + "\ndifficulty: " + this.getDifficulty() + "\nNonce: " + this.nonce
+ "\n-----------------------\n";
}
/**
* Mine a block based on given last block and data until a block hash is found
* that meets the leading 0's Proof of Work requirement.
*
* #param last_block
* #param data
* #return
* #throws NoSuchAlgorithmException
*/
public static Block mine_block(Block last_block, String[] data) throws NoSuchAlgorithmException {
long timestamp = new Date().getTime();
String last_hash = last_block.getHash();
int difficulty = Block.adjust_difficulty(last_block, timestamp);
int nonce = 0;
String hash = CryptoHash.getSHA256(timestamp, last_block.getHash(), data, difficulty, nonce);
String proof_of_work = CryptoHash.n_len_string('0', difficulty);
// System.out.println("Proof of work " + proof_of_work);
String binary_hash = CryptoHash.hex_to_binary(hash);
// System.out.println("binary hash " + binary_hash);
String binary_hash_work_end = binary_hash.substring(0, difficulty);
// System.out.println("binary_Hash_work_end " + binary_hash_work_end);
System.out.println("Difficulty: " + difficulty);
while (!proof_of_work.equalsIgnoreCase(binary_hash_work_end)) {
// System.out.println("Working");
nonce += 1;
timestamp = new Date().getTime();
difficulty = Block.adjust_difficulty(last_block, timestamp);
hash = CryptoHash.getSHA256(timestamp, last_block.getHash(), data, difficulty, nonce);
proof_of_work = CryptoHash.n_len_string('0', difficulty);
binary_hash = CryptoHash.hex_to_binary(hash);
binary_hash_work_end = binary_hash.substring(0, difficulty);
// System.out.println(binary_hash_work_end);
// System.out.println(binary_hash);
// System.out.println(proof_of_work);
}
System.out.println("Solved at Difficulty: " + difficulty);
// System.out.println("Proof of work requirement " + proof_of_work);
// System.out.println("binary_Hash_work_end " + binary_hash_work_end);
// System.out.println("binary hash " + binary_hash);
System.out.println("BLOCK MINED");
return new Block(timestamp, last_hash, hash, data, difficulty, nonce);
}
/**
* Generate Genesis block
*
* #return
*/
public static Block genesis_block() {
long timestamp = 1;
String last_hash = "genesis_last_hash";
String hash = "genesis_hash";
String[] data = { "buy", "privcoin" };
int difficulty = 4;
int nonce = 0;
return new Block(timestamp, last_hash, hash, data, difficulty, nonce);
}
/**
* Calculate the adjusted difficulty according to the MINE_RATE. Increase the
* difficulty for quickly mined blocks. Decrease the difficulty for slowly mined
* blocks.
*
* #param last_block
* #param new_timestamp
*/
public static int adjust_difficulty(Block last_block, long new_timestamp) {
long time_diff = new_timestamp - last_block.getTimestamp();
// System.out.println(time_diff);
if (time_diff < MINE_RATE) {
// System.out.println("Increasing difficulty");
return last_block.getDifficulty() + 1;
} else if (last_block.getDifficulty() - 1 > 0) {
// System.out.println("Decreasing difficulty");
return last_block.getDifficulty() - 1;
} else {
return 1;
}
}
/**
* Validate block by enforcing following rules: - Block must have the proper
* last_hash reference - Block must meet the proof of work requirements -
* difficulty must only adjust by one - block hash must be a valid combination
* of block fields
*
* #param last_block
* #param block
* #return
* #throws NoSuchAlgorithmException
*/
public static boolean is_valid_block(Block last_block, Block block) throws NoSuchAlgorithmException {
String binary_hash = CryptoHash.hex_to_binary(block.getHash());
char[] pow_array = CryptoHash.n_len_array('0', block.getDifficulty());
char[] binary_char_array = CryptoHash.string_to_charray(binary_hash);
if (!block.getLastHash().equalsIgnoreCase(last_block.getHash())) {
System.out.println("The last hash must be correct");
return false;
// Throw exception the last hash must be correct
}
if (!Arrays.equals(pow_array, Arrays.copyOfRange(binary_char_array, 0, block.getDifficulty()))) {
System.out.println("Proof of work requirement not met");
return false;
// throw exception - proof of work requirement not met
}
if (Math.abs(last_block.difficulty - block.difficulty) > 1) {
System.out.println("Block difficulty must adjust by one");
return false;
// throw exception: The block difficulty must only adjust by 1
}
String reconstructed_hash = CryptoHash.getSHA256(block.getTimestamp(), block.getLastHash(), block.getData(),
block.getDifficulty(), block.getNonce());
if (!block.getHash().equalsIgnoreCase(reconstructed_hash)) {
System.out.println("The block hash must be correct");
System.out.println(block.getHash());
System.out.println(reconstructed_hash);
return false;
// throw exception: the block hash must be correct
}
System.out.println("You have mined a valid block");
return true;
}
public int getDifficulty() {
return difficulty;
}
public long getTimestamp() {
return timestamp;
}
public String getHash() {
return hash;
}
public String getLastHash() {
return lastHash;
}
public String[] getData() {
return data;
}
public int getNonce() {
return nonce;
}
public static void main(String[] args) throws NoSuchAlgorithmException {
// String md = CryptoHash.getSHA256("foobar");
Block genesis = genesis_block();
System.out.println(genesis.toString());
// Block bad_block = Block.mine_block(genesis, new String[] { "watch", "AOT" });
// bad_block.lastHash = "evil data";
// System.out.println(bad_block.toString());
Block good_block = mine_block(genesis, new String[] { "foo", "bar" });
System.out.println(good_block.toString());
// System.out.println(mine_block(new_block, new String[] { "crypto", "is", "fun" }).toString());
// System.out.println(Block.is_valid_block(genesis, bad_block)); // returns false as expected
System.out.println(Block.is_valid_block(genesis, good_block));
System.out.println(CryptoHash.hex_to_binary(good_block.getHash()));
Block good_block2 = mine_block(good_block, new String[] { "bar", "foo" });
Block good_block3 = mine_block(good_block2, new String[] { "bar", "foo" });
Block good_block4 = mine_block(good_block3, new String[] { "bar", "foo" });
// Block good_block5 = mine_block(good_block4, new String[] {"bar", "foo"});
// Block good_block6 = mine_block(good_block5, new String[] {"bar", "foo"});
}
}
package privblock.gerald.ryan.util;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
public class CryptoHash {
static HashMap<Character, String> HEX_TO_BIN_TABLE;
static {
HEX_TO_BIN_TABLE = new HashMap<Character, String>();
HEX_TO_BIN_TABLE.put('0', "0000");
HEX_TO_BIN_TABLE.put('1', "0001");
HEX_TO_BIN_TABLE.put('2', "0010");
HEX_TO_BIN_TABLE.put('3', "0011");
HEX_TO_BIN_TABLE.put('4', "0100");
HEX_TO_BIN_TABLE.put('5', "0101");
HEX_TO_BIN_TABLE.put('6', "0110");
HEX_TO_BIN_TABLE.put('7', "0111");
HEX_TO_BIN_TABLE.put('8', "1000");
HEX_TO_BIN_TABLE.put('9', "1001");
HEX_TO_BIN_TABLE.put('a', "1010");
HEX_TO_BIN_TABLE.put('b', "1011");
HEX_TO_BIN_TABLE.put('c', "1100");
HEX_TO_BIN_TABLE.put('d', "1101");
HEX_TO_BIN_TABLE.put('e', "1110");
HEX_TO_BIN_TABLE.put('f', "1111");
}
public static String getSHA256(String... sarray) throws NoSuchAlgorithmException {
String s = concat(sarray);
// System.out.printf("Hashing \"%s\"\n", s);
MessageDigest md;
md = MessageDigest.getInstance("SHA-256");
byte[] b = md.digest(s.getBytes(StandardCharsets.UTF_8));
BigInteger number = new BigInteger(1, b);
StringBuilder hexString = new StringBuilder(number.toString(16));
while (hexString.length() < 32) {
hexString.insert(0, '0');
}
String mds = hexString.toString();
// System.out.printf("hash is:\n%s\n", mds);
return hexString.toString();
}
public static String getSHA256(long timestamp, String last_hash, String[] data, int difficulty, int nonce)
throws NoSuchAlgorithmException {
String s = "";
s += Long.toString(timestamp);
s += last_hash;
s += concat(data);
s += Integer.toString(difficulty);
s += Integer.toString(nonce);
// System.out.printf("Hashing \"%s\"\n", s);
MessageDigest md;
md = MessageDigest.getInstance("SHA-256");
byte[] b = md.digest(s.getBytes(StandardCharsets.UTF_8));
BigInteger number = new BigInteger(1, b);
StringBuilder hexString = new StringBuilder(number.toString(16));
// System.out.println(hexString);
while (hexString.length() < 32) {
hexString.insert(0, '0');
}
String messageDigestString = hexString.toString();
// System.out.printf("hash is:\n%s\n", messageDigestString);
return hexString.toString();
}
public static char[] n_len_array(char c, int n) {
char[] ch = new char[n];
for (int i = 0; i<n; i++) {
ch[i] = c;
}
return ch;
}
public static String n_len_string(char c, int n) {
String s = "";
for (int i = 0; i<n; i++) {
s += c;
}
return s;
}
public static String concat(String... args) {
String s = "";
for (String $ : args) {
s += $;
}
// System.out.println(s);
return s;
}
public static char[] string_to_charray(String str) {
char[] ch = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
ch[i] = str.charAt(i);
}
return ch;
}
public static String string_to_hex(String arg) {
return String.format("%064x", new BigInteger(1, arg.getBytes(StandardCharsets.UTF_8)));
}
public static String hex_to_binary(String hex_string) {
String binary_string = "";
for (int i = 0; i < hex_string.length(); i++) {
binary_string += HEX_TO_BIN_TABLE.get(hex_string.charAt(i));
}
return binary_string;
}
public static String string_to_binary(String raw_string) {
String hex_string = string_to_hex(raw_string);
String bin_string = hex_to_binary(hex_string);
return bin_string;
}
}
ps here's an example of a log I created. I created other cleaner logs too but this shows what we're working with. The first item represents time in milliseconds. The second represents the first four digits of the hash, which is directly below it, followed by the level of difficulty requirement string (what the second item needs to be, length n = difficulty level). The hash just never leads with four zeros, ever, so my hash function or call to the function must be broken in some way.
6479
1000
1000001010111011100110111010100100111010101001111110010101011101101101110000110100110110110000001010001000000010110001100111100111010100110001001001110111011010011100110000011111110100000100000100000010100001000110000111000101100010001111011000110011111101
0000
6479
0101
0101110111010100101010100000001011100011000001110001011011001101001111101011010011000111101101111111001001001010100110101101100111111011001011100101111000011100010001000000000011000111010000101101001000001010101010111001010000101001110011111101011011011000
0000
6479
1000
1000000001000101001110001110110000110111001101100001011000111010111110001011011010011111111101011001110011001001111011011110110010101010101100011011001001110001100010010101001011100001101011011101010000000100111100011011110100000101100111010100100110011101
0000
6479
I figured out the problem. It is indeed often returning 4 leading zeroes but the code as structured is clipping them off (because it doesn't think they have meaning). I noticed by logging that the length is not always a fixed 64byte/256 bit string. Here's the output:
256
1101111000010000100001110001010001010000001010111001100011010011110010001001010001010010100110111000110010000010001110110100100101000000001111111110011100000001010100000111001000111101010001010100110100000000111000100001000000010010010111011110110011110111
256
011001111101001000011111011001111110010110000011001011111010001011010110010100001011010011010010111101100010010111000010110010110111110001010101100000000101001000111110100111011100001110010010101011011000000101100001101110101101010001110000111111110000
252
0001100101110011101000000011000101011100111101110100111110100101110110011100010110001011000110010011110110011001100111010001100100011001011000001011100011011011011011101000111000011100100011011011011000101010011101000110101011000110011100111010000011000011
256
1100110001001001110001100111100010101100100010110111100111001010011011111111100010100110110000010000101000010111111010010101110001100010101010111111111111001011010111010100001010000010111100100100111000010101011000110000100000100111010001000011000000010000
256
So that's solved, or at least I understand the problem. It's amazing what sleep will do.
I wanted to mock a static method used inside a private method of a class and return a specific value.
public class Test {
private String encodeValue(String abc) {
try {
return URLEncoder.encode(...);
} catch (UnsupportedEncodingException e) {
throw InvalidValueException.create("Error converting");
}
}
URLEncoder.encode ->encode is a static method inside URLEncoder.
In Test class using powermock works:
PowerMock.mockStatic(URLEncoder.class);
expect(URLEncoder.encode()).andThrow(new UnsupportedEncodingException());
PowerMock.replay(URLEncoder.class);
String encoded = Whitebox.invokeMethod(testMock,"encodeVaue","Apple Mango");
But i wanted to replace Powermock with any other mocking ways available.
Is there a way to mock the above class.
URL Encoder class:
/**
* Translates a string into {#code application/x-www-form-urlencoded}
* format using a specific encoding scheme. This method uses the
* supplied encoding scheme to obtain the bytes for unsafe
* characters.
* <p>
* <em><strong>Note:</strong> The <a href=
* "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
* World Wide Web Consortium Recommendation</a> states that
* UTF-8 should be used. Not doing so may introduce
* incompatibilities.</em>
*
* #param s {#code String} to be translated.
* #param enc The name of a supported
* <a href="../lang/package-summary.html#charenc">character
* encoding</a>.
* #return the translated {#code String}.
* #exception UnsupportedEncodingException
* If the named encoding is not supported
* #see URLDecoder#decode(java.lang.String, java.lang.String)
* #since 1.4
*/
public static String encode(String s, String enc)
throws UnsupportedEncodingException {
boolean needToChange = false;
StringBuffer out = new StringBuffer(s.length());
Charset charset;
CharArrayWriter charArrayWriter = new CharArrayWriter();
if (enc == null)
throw new NullPointerException("charsetName");
try {
charset = Charset.forName(enc);
} catch (IllegalCharsetNameException e) {
throw new UnsupportedEncodingException(enc);
} catch (UnsupportedCharsetException e) {
throw new UnsupportedEncodingException(enc);
}
for (int i = 0; i < s.length();) {
int c = (int) s.charAt(i);
//System.out.println("Examining character: " + c);
if (dontNeedEncoding.get(c)) {
if (c == ' ') {
c = '+';
needToChange = true;
}
//System.out.println("Storing: " + c);
out.append((char)c);
i++;
} else {
// convert to external encoding before hex conversion
do {
charArrayWriter.write(c);
/*
* If this character represents the start of a Unicode
* surrogate pair, then pass in two characters. It's not
* clear what should be done if a bytes reserved in the
* surrogate pairs range occurs outside of a legal
* surrogate pair. For now, just treat it as if it were
* any other character.
*/
if (c >= 0xD800 && c <= 0xDBFF) {
/*
System.out.println(Integer.toHexString(c)
+ " is high surrogate");
*/
if ( (i+1) < s.length()) {
int d = (int) s.charAt(i+1);
/*
System.out.println("\tExamining "
+ Integer.toHexString(d));
*/
if (d >= 0xDC00 && d <= 0xDFFF) {
/*
System.out.println("\t"
+ Integer.toHexString(d)
+ " is low surrogate");
*/
charArrayWriter.write(d);
i++;
}
}
}
i++;
} while (i < s.length() && !dontNeedEncoding.get((c = (int) s.charAt(i))));
charArrayWriter.flush();
String str = new String(charArrayWriter.toCharArray());
byte[] ba = str.getBytes(charset);
for (int j = 0; j < ba.length; j++) {
out.append('%');
char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16);
// converting to use uppercase letter as part of
// the hex value if ch is a letter.
if (Character.isLetter(ch)) {
ch -= caseDiff;
}
out.append(ch);
ch = Character.forDigit(ba[j] & 0xF, 16);
if (Character.isLetter(ch)) {
ch -= caseDiff;
}
out.append(ch);
}
charArrayWriter.reset();
needToChange = true;
}
}
return (needToChange? out.toString() : s);
}
Mocking privates and statics is one of the chief strengths of JMockit over other mocking frameworks.
The class you call "Test" is really the "ClassUnderTest", so apologies, but the test of "Test" is "TestTest" :)
public class TestTest {
#Tested
public Test cut;
#Test
public void testencodeValue() {
// Mock the static
new MockUp<URLEncoder>() {
#Mock
String encode(String s, String enc) {
return "JMockit FTW";
}
};
// invoke the private method
final Method method = MethodReflection.findCompatibleMethod(Test.class, "encodeValue", new Class<?>[] { String.class });
final String res = MethodReflection.invoke(cut, method);
assertEquals("JMockit FTW", res);
}
}
That said, testing privates is sort of a PITA. I am generally of the mind that if a method is worth testing, it is almost certainly worth exposing. The same criteria that make the method worth testing means that somebody-somewhere-someday will want to override your implementation and provide a slightly alternative one. Make their job easy, and make it protected. Make your (testing) job easy, and do the same thing.
I have a project for Hangman game application that reads guesses from the console input and to make unit testing for it, but i don't know how to test it. They told me to make it return true or false on the last method, because my code is untestable and that will help me to test it like i play the game /to win and to lose/. But i don't know how to do it. Can someone help me to test it?
here is my code:
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static Scanner scanner = new Scanner(System.in);
private String secredWord;
private StringBuilder dashes;
private int lives;
private int guesses;
private char[] wrongGuess;
/**
* Generates a string with all letters in the searched word replaced with an
* underline and a space.
*
* #return return the secred word hidden.
*/
public StringBuilder makeDashes() {
dashes = new StringBuilder();
for (int i = 0; i < secredWord.length(); i++) {
dashes.append("_ ");
}
return dashes;
}
/**
* Takes a string as a parameter and set the sacred word to be guessed, and
* reset the lives & guesses that are made.
*
* #param secredWord
* the word that has to be guessed.
*
* #return - returns the method that make the word hidde.
*/
public StringBuilder setGame(String secredWord) {
this.secredWord = secredWord;
lives = 0;
guesses = 0;
wrongGuess = new char[6];
return makeDashes();
}
/**
* Make a check for a repeatable guesses.
*
* #param guess
* Characters to check
* #return - returns true or false. If true it make you guess another
* character.
*/
private boolean repeatGuesses(char guess) {
for (int i = 0; i < dashes.length(); i = i + 2) {
if (dashes.charAt(i) == guess) {
return true;
}
}
for (int i = 0; i < wrongGuess.length; i++) {
if (wrongGuess[i] == guess) {
return true;
}
}
return false;
}
/**
* Reads a letter and check if its been guessed before.
*/
private void guessLetter() {
char guess;
boolean present = false;
LOGGER.info("Make a guess: ");
guess = scanner.next().charAt(0);
while (repeatGuesses(guess)) {
LOGGER.info("The letter has been guessed already!");
LOGGER.info("Make another guess: ");
guess = scanner.next().charAt(0);
}
for (int i = 0; i < secredWord.length(); i++) {
if (secredWord.charAt(i) == guess) {
dashes.setCharAt(i * 2, guess);
present = true;
guesses++;
}
}
if (!present) {
LOGGER.info("Wrong guess");
wrongGuess[lives++] = guess;
}
}
/**
* While you didnt guess the word or you have more lifes left calls the
* motod guessLetter() and you have to make a guess untill you lost your
* lifes or guess the word.
*/
public void playTheGame() {
while (lives < 6 && guesses < secredWord.length()) {
LOGGER.info("Hidden word --> {}", dashes);
LOGGER.info("Lives: ({}/6 wrong letters)", lives);
guessLetter();
}
if (lives == 6) {
LOGGER.info("You lost!The word was --> {}", secredWord);
} else {
LOGGER.info("Congratulations YOU WON!!! The word was --> {}.", secredWord);
}
}
And make one test to see is the word hidden `
public void hiddenWordTest() {
HangmanGame item = new HangmanGame();
String secredWord = "hangman";
String correctHidden = "_ _ _ _ _ _ _ ";
StringBuilder resultHidden = item.setGame(secredWord);
assertEquals(correctHidden, resultHidden.toString());
}`
I can think of two options:
You can pass in LOGGER via contructor and fake it during a test
Extract computation logic into separate class and test that in isolation without any fakes.
I have downloaded a java file needed for a coursework at college. However I find it impossible to run it. Eclipse won't give me the chance to even run it (only ant build), and if I use netbeans I get this exception :
Exception in thread "main"
java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Uncompilable source code - class Hangman is public, should be declared in a file named Hangman.java
at Hangman. < clinit > (hangman(Case Conflict).java: 20)
Java Result: 1
If someone is kind enough to read through the code, I really do not know what to do next. I figure there has to be something wrong with the main class. Thanks!
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
class Hangman {
Scanner userInput;
private Set < Character > wrongGuesses;
private String[] answers = {
"leverets", "hatchlings", "puppies",
"kittens", "pullets", "goslings"
};
private String answer;
private String guessed;
private int maxTurns;
private int currentTurns;
private boolean inProgress;
private char nextGuess;
private boolean gameWin;
public Hangman() {
userInput = new Scanner(System.in);
wrongGuesses = new HashSet < Character > ();
inProgress = false;
gameWin = false;
maxTurns = 14;
currentTurns = 0;
// set answer somehow
answer = answers[0];
// set guessed to the correct number of dashes
StringBuilder sb = new StringBuilder();
for (int i = 0; i < answer.length(); i++) {
sb.append('-');
}
guessed = sb.toString();
}
/* start a new game */
public void startGame() {
inProgress = true;
startGameLoop();
}
/* the game loop. this method is the heart of the game */
private void startGameLoop() {
printInstructions();
while (inProgress) {
printStatus();
acceptGuess();
checkStatus();
}
printWinOrLose();
}
private void printInstructions() {
System.out
.println("Guess the word one letter at a time until you win or run out of turns. Good luck!");
}
private void printWinOrLose() {
if (gameWin) {
System.out.println("You win! The answer was " + answer);
} else {
System.out.println("You lose.");
}
}
private void printStatus() {
System.out.println("Guesses left: " + (maxTurns - currentTurns));
System.out.println("Current status: " + guessed);
System.out.println("Wrong guesses: " + getWrongAnswers());
}
/* get the next character from the player */
private void acceptGuess() {
System.out.println("Next guess: ");
String temp = userInput.next();
nextGuess = temp.charAt(0);
}
/* check what state the game is in */
private void checkStatus() {
// if already guessed, say already guessed.
if (wrongGuesses.contains(nextGuess)) {
System.out.println("You already guessed that!");
return;
}
// if guess is not in answer, update number of turns played and add
// guess to wrong guesses
// otherwise update the guessed variable
if (answer.indexOf(nextGuess) < 0) {
++currentTurns;
wrongGuesses.add(nextGuess);
} else {
updateGuessStatus();
}
// check to see if the player has won or lost
if (answer.equals(guessed)) {
gameWin = true;
inProgress = false;
}
if (currentTurns == maxTurns) {
inProgress = false;
}
}
/* update the guessed variable when there is a correct guess made */
private void updateGuessStatus() {
// replace - with nextGuess where appropriate
int index = answer.indexOf(nextGuess);
int lastIndex = answer.lastIndexOf(nextGuess);
StringBuilder sb = new StringBuilder(guessed);
if (index != lastIndex) { // more than one instance of the guess in the
// answer
// swap out in a loop
while (index != -1) {
sb.setCharAt(index, nextGuess);
int i = answer.indexOf(nextGuess, (index + 1));
index = i;
}
} else { // letter only appears once
// swap out just that one
sb.setCharAt(index, nextGuess);
}
guessed = sb.toString();
}
/* build a text representation of all the incorrect guesses */
private String getWrongAnswers() {
if (wrongGuesses.size() > 0) {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (Character c: wrongGuesses) {
sb.append(c + ",");
}
sb.deleteCharAt(sb.length() - 1); // delete trailing comma
sb.append(')');
return sb.toString();
} else {
return "<none>";
}
}
public static void main(String[] args) {
Hangman h = new Hangman();
h.startGame();
}
}
The exception says everything you need to know. Rename the class FILE to Hangman.java.
Caused by: java.lang.RuntimeException: Uncompilable source code - class Hangman is public, should be declared in a file named Hangman.java
You should save your downloaded file in Hangman.java and not hangman.java (see it needs 'H' in caps same as your class name).
Change the class to public class Hangman. It allows outside methods to access it.
EDIT: I downloaded the file, changing the class to public worked. I also found an issue in the code itself, the word is always "Leverets".
To change this, edit the getAnswer() method and change it to
private int getAnswer() {
int i = (int) (Math.random() * 6) + 0;
return i;
}
Hi Team, I am trying to find a String "Henry" in a binary file and change the String to a different string. FYI the file is the output of serialisation of an object. Original Question here
I am new to searching bytes and imagined this code would search for my byte[] and exchange it. But it doesn't come close to working it doesn't even find a match.
{
byte[] bytesHenry = new String("Henry").getBytes();
byte[] bytesSwap = new String("Zsswd").getBytes();
byte[] seekHenry = new byte[bytesHenry.length];
RandomAccessFile file = new RandomAccessFile(fileString,"rw");
long filePointer;
while (seekHenry != null) {
filePointer = file.getFilePointer();
file.readFully(seekHenry);
if (bytesHenry == seekHenry) {
file.seek(filePointer);
file.write(bytesSwap);
break;
}
}
}
Okay I see the bytesHenry==seekHenry problem and will swap to Arrays.equals( bytesHenry , seekHenry )
I think I need to move along by -4 byte positions each time i read 5 bytes.
Bingo it finds it now
while (seekHenry != null) {
filePointer = file.getFilePointer();
file.readFully(seekHenry);;
if (Arrays.equals(bytesHenry,
seekHenry)) {
file.seek(filePointer);
file.write(bytesSwap);
break;
}
file.seek(filePointer);
file.read();
}
The following could work for you, see the method search(byte[] input, byte[] searchedFor) which returns the index where the first match matches, or -1.
public class SearchBuffer {
public static void main(String[] args) throws UnsupportedEncodingException {
String charset= "US-ASCII";
byte[] searchedFor = "ciao".getBytes(charset);
byte[] input = "aaaciaaaciaojjcia".getBytes(charset);
int idx = search(input, searchedFor);
System.out.println("index: "+idx); //should be 8
}
public static int search(byte[] input, byte[] searchedFor) {
//convert byte[] to Byte[]
Byte[] searchedForB = new Byte[searchedFor.length];
for(int x = 0; x<searchedFor.length; x++){
searchedForB[x] = searchedFor[x];
}
int idx = -1;
//search:
Deque<Byte> q = new ArrayDeque<Byte>(input.length);
for(int i=0; i<input.length; i++){
if(q.size() == searchedForB.length){
//here I can check
Byte[] cur = q.toArray(new Byte[]{});
if(Arrays.equals(cur, searchedForB)){
//found!
idx = i - searchedForB.length;
break;
} else {
//not found
q.pop();
q.addLast(input[i]);
}
} else {
q.addLast(input[i]);
}
}
return idx;
}
}
From Fastest way to find a string in a text file with java:
The best realization I've found in MIMEParser: https://github.com/samskivert/ikvm-openjdk/blob/master/build/linux-amd64/impsrc/com/sun/xml/internal/org/jvnet/mimepull/MIMEParser.java
/**
* Finds the boundary in the given buffer using Boyer-Moore algo.
* Copied from java.util.regex.Pattern.java
*
* #param mybuf boundary to be searched in this mybuf
* #param off start index in mybuf
* #param len number of bytes in mybuf
*
* #return -1 if there is no match or index where the match starts
*/
private int match(byte[] mybuf, int off, int len) {
Needed also:
private void compileBoundaryPattern();