Treating a bidimensional text (Ruzzle solver) - java

I am trying to program a Ruzzle solver Java application for learning purpose.
I have a little problem in th ematter of "finding words" in a Ruzzle-type map.
Example of a Ruzzle map (it is composed of 4 rows and 4 columns of 1 letter in each cell) :
Z O O H
E Y L H
I E L I
H O F M
http://www.maclife.com/files/imagecache/futureus_imagegallery_fullsize/gallery/ruzzle1.jpg
I would like to obtain a list of all possible words you can find in such a map.
The difficulty : you can find a word by appending letters vertically, horizontally and diagonally (example : "HELLO").
So far, I created 3 classes :
Ruzzlesolver.java
Letter.java
Map.java
The Letter class
Describes a single Letter of the map, its fields are the X and Y positions and the character of the cell.
The Ruzzlesolver class
This is the main class.
it reads the Ruzzle map (an line by line input in the console)
it reads the dictionnary.txt file
it compares the map with the dictionnary file
it writes into the results.txt file
Each line is stored in a char array.
Then I create a new Map object from the 4 obtained arrays.
The Map class
This is the constructor of the Map objects :
public Map(final char[] pTab1, final char[] pTab2, final char[] pTab3, final char[] pTab4)
{
this.aLettres = new ArrayList<Letter>();
for (int i = 0 ; i < 4 ; i++) {
this.aLettres.add(new Letter(1, i+1, pTab1[i]));}
for (int i = 0 ; i < 4 ; i++) {
this.aLettres.add(new Letter(2, i+1, pTab2[i]));}
for (int i = 0 ; i < 4 ; i++) {
this.aLettres.add(new Letter(3, i+1, pTab3[i]));}
for (int i = 0 ; i < 4 ; i++) {
this.aLettres.add(new Letter(4, i+1, pTab4[i]));}
}
this.aLettres is the ArrayList that contains each of the 16 letters of the map.
Each Letter knows its column (X position : "i+1"), its row (Y position : "1, 2, 3 and 4") and its character ("pTab[i]").
Now that we know the map and the place of each letter, we can begin to find the words.
The contains() method
This is my problem : I am stuck using the following method :
How it is called
I pick a word from the dictionnary in the Ruzzlesolver class.
I call the contains() method on my Map object, with this word as a parameter :
if (this.aMap.contains(vMot)) {/*print vMot in the result.txt file*/}
How does the contains() method work
Variables :
char[] vChars = new char[pMot.length()];
ArrayList<Letter> vFoundCharS1 = new ArrayList<Letter>();
Stocking each characters of pMot in an ArrayList :
for (int i = 0 ; i < pMot.length() ; i++) {
vChars[i] = pMot.charAt(i);
}
Searching for the first character of pMot :
for (Letter vL : this.aLettres) {
if (vL.getChar() == vChars[0]) {
vFoundCharS1.add(vL);
return true;
}
}
I am stuck.
If I continue this method, I will have to create longer and longer blocks as I progress. Besides, I would need to write 16 blocks to consider every length possibility.
I am sure this is a wrong method. How would you implement such a treatment ?
Thanks a lot in advance for your help.
PS : I apologize for grammar/English mistakes, English is not my natal language.

If I understand the problem correctly, you can pick every adjacent cell for the next letter, right? In that case, the code below would (I think) solve your problem.
I changed the constructor of Map, because it is easier to work with a two-dimensional array of char.
The function contains does just what your step 3 described: find the first letter and try searching on from there. The function findRecursively searches for the rest of the word recursively.
public class Map {
private char[][] board;
public Map(final char[] pTab1, final char[] pTab2,
final char[] pTab3, final char[] pTab4) {
board = new char[4][4];
for (int i = 0 ; i < 4 ; i++) {
board[0][i] = pTab1(i);
board[1][i] = pTab2(i);
board[2][i] = pTab3(i);
board[3][i] = pTab4(i);
}
}
public boolean contains(String word) {
char[] array = word.toCharArray();
// empty string is trivial
if (array.length == 0)
return true;
for(int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (board[i][j] == array[0] && findRecursively(i, j, array, 1))
return true;
}
}
return false;
}
public boolean isValid(int i, int j) {
return (0 <= i && i < 4) && (0 <= j && j < 4);
}
public boolean findRecursively(int i, int j, char[] array, int index) {
// reached end of word
if (index == array.length) {
return true;
} else {
// loop over all neighbors
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
// skip cell itself and invalid cells
if (!(di == 0 && dj == 0) && isValid(i+di, j+dj)) {
if (board[i+di][j+dj] == array[index]
&& findRecursively(i+di, j+dj, array, index+1))
return true;
}
}
}
return false;
}
}
}

Related

First Unique Character In a String-Leetcode

I have tried out 387.First Unique Character In A string
Given a string s, find the first non-repeating character in it and
return its index. If it does not exist, return -1.
EXAMPLE : 1
Input: s = "leetcode"
Output: 0
EXAMPLE :2
Input: s = "loveleetcode"
Output: 2
I have been trying this problem. I thought we will pick one by one all the characters and check if a repeating character exists break from the loop. And if not then return that index.I have thought over a solution which I believe is not the most efficient way but I want to know the how can I solve this problem with the approach given below:
public int firstUniqChar(String s) {
for(int i=0;i<s.length();i++){
for(int j=i+1;j<s.length();j++){
if(s.charAt(i)==s.charAt(j)){
break;
}
}
}
return -1;
}
I'm confused how to return the index.I'm unable to find the logic after:
for(int j=i+1;j<s.length();j++){
if(s.charAt(i)==s.charAt(j)){
break;
}
}
If anyone can help me find out the logic here.
Try this.
public static int firstUniqChar(String s) {
L: for (int i = 0, length = s.length(); i < length; i++) {
for (int j = 0; j < length; j++)
if (i != j && s.charAt(i) == s.charAt(j))
continue L;
return i;
}
return -1;
}
public static void main(String[] args) {
System.out.println(firstUniqChar("leetcode"));
System.out.println(firstUniqChar("loveleetcode"));
System.out.println(firstUniqChar("aabb"));
}
output:
0
2
-1
you can use a flag variable.
public int firstUniqChar(String s) {
int flag=0;
for(int i=0;i<s.length();i++){
flag=0;
for(int j=0;j<s.length();j++){
if(s.charAt(i)==s.charAt(j) && i!=j){
flag=1;
break;
}
}
if(flag==0){
return i;
}
}
return -1;
}
There are 26 possible lowercase English letters, so you could use two 26 element arrays.
One array, letterCount, keeps counts of each letter. Start at 0 and add 1 every time the corresponding letter appears in the text string. The second array, position, holds the position of the first occurrence of that letter, or -1 if the letter never appears. You will need to initialise that array to -1 for all elements.
Process the string in order, recording initial positions, once only for each letter, and incrementing the count for each letter in the string.
After the string has been processed, look through the letterCount array. If there are no letters with a 1 count then return -1. If exactly one letter has a 1 count, then return the position of that letter from the position array. If more than one letter has a 1 count, then pick the one with the lowest value for its position.
Using two loops is a highly inefficient way of solving this problem. The string can be up to 100,000 characters long and you are processing it multiple times. Far better to process it only once, keeping track of what you have found so far.
Fix you code
You need to add a variable that tells you if you have breaked the loop or not
static int firstUniqChar(String s) {
boolean duplicate;
for (int i = 0; i < s.length(); i++) {
duplicate = false;
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
duplicate = true;
break;
}
}
if (!duplicate) {
return i;
}
}
return -1;
}
Improve
There is a smarter way, that is finding the last index occurence of the current char, if it's equal to the current index : that char is unique and you return its index
static int firstUniqChar(String s) {
for (int i = 0; i < s.length(); i++) {
if (s.lastIndexOf(s.charAt(i)) == i) {
return i;
}
}
return -1;
}
If you do not bother about time complexity using IdenxOF operations, then one can try this solution.
indexOf() – also runs in linear time. It iterates through the internal array and checking each element one by one. So the time
complexity for this operation always requires O(n) time.
int firstUniqCharOneLoop(String str) {
for (int i = 0; i < str.length(); i++) {
if (str.indexOf(str.charAt(i))== str.lastIndexOf(str.charAt(i)) ) {
return i;
}
}
return -1;
}
The lowest complexity I managed to achieve:
public class UniqueSymbolFinder {
static int findFirstUniqueChar(String s) {
Set<Character> set = new HashSet<>(s.length());
List<CharWithIndex> candidates = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
CharWithIndex charWithIndex = new CharWithIndex(ch, i);
if (set.add(ch)) {
candidates.add(charWithIndex);
} else {
candidates.remove(charWithIndex);
}
}
return candidates.size() == 0 ? -1 : candidates.get(0).index;
}
/**
* Class for storing the index.
* Used to avoid of using an indexOf or other iterations.
*/
private static class CharWithIndex {
int index;
char ch;
private CharWithIndex(char ch, int index) {
this.ch = ch;
this.index = index;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CharWithIndex that = (CharWithIndex) o;
return ch == that.ch;
}
#Override
public int hashCode() {
return Objects.hash(ch);
}
}
}
I believe the memory usage can still be optimized.
100% Correct JAVA Solution
Since the question is about returning the index of the first non-repeating character in a string, we need some data structure to save the index of each character in the string for us.
I choose here the HashMap of Java. Basically, what you can do with it, you can save a pair of values (or pair of other data structures).
So, in my solution, I am saving a Character Integer pair. The first is considered as a key (here it is each character in the string), and the second is its index value.
The problem here is that we only want to keep the minimum index of non-repeating characters and that's why if you take a look below, you will find the maxIndexForRepeatedValues is set to be 10 power 5 as the input constraint says 1 <= s.length <= 10 power 5.
However, I am using that value to neglect repeated characters that would be found in the HashMap and at the end, we retrieve the minimum index which is the index of course for the first character from the map, or if there were only repeated characters, we return -1.
To make the code shorter, I used ternary-operator but you can write it with if-else if you want!
class Solution {
public int firstUniqChar(String s) {
int maxIndexForRepeatedValues = 100000;
Map<Character, Integer> map = new HashMap<>();
for (int i = 0 ; i < s.length() ; i++) {
char key = s.charAt(i);
int resIndex = map.containsKey(key) ? maxIndexForRepeatedValues : i;
map.put(key, resIndex);
}
int minIndex = Collections.min(map.values());
return minIndex == maxIndexForRepeatedValues ? -1 : minIndex;
}
}

Java maze inside of walls and get all possible paths

I know that there's a lot of other maze solver here. Though I would like to have my own approach and I think my problem is a bit different from the others.
As of now, here's what I've started and hopefully I can achieve what I have in mind at the moment.
private static int getPossiblePaths(File f) throws IOException {
int counts = 0; // hope to return all possible paths
// read input file then put it on list string
List<String> lines = Files.lines(f.toPath()).collect(Collectors.toList());
// get the row and column (dimensions)
String[] dimensions = lines.get(0).split(",");
//initalize sub matrix of the maze dimensions and ignoring the top and bottom walls
int[][] mat = new int[Integer.valueOf(dimensions[0]) - 2 ][Integer.valueOf(dimensions[1]) - 2];
//for each line in the maze excluding the boundaries (top and bottom)
for( int i = 2 ; i < lines.size() - 1 ; i++) {
String currLine = lines.get(i);
int j = 0;
for(char c : currLine.toCharArray()) {
mat[i-2][j] = (c=='*' ? 'w' : c=='A' ? 'a' : c=='B' ? 'b' : 's');
// some conditional statements here
}
}
// or maybe some conditional statements here outside of the loop
return counts;
}
And the maze from a text file is look like this. Please note that the A could be anywhere and same as B. The only movements allowed is to right and down.
5,5
*****
*A *
* *
* B*
*****
Expected output for the maze above is 6 (possible paths from A to B).
EDIT: Also the maze from the text file could be like this:
8,5
********
* A *
* B*
* *
********
So with my current code, it is getting the dimensions (first line) and removing the top and bottom part of the maze (boundaries). So there's only 3 lines of characters currently stored in the mat array. And some encoding of each characters of the text file (#=w(wall), A=a(start), B=b(end), else s(space))
I would like to have some conditional statements inside of the foreach to probably store the each of characters inside of an ArrayList. Though I'm not sure if this approach will just make my life harder.
Any suggestions, tips, advice or other easier approach from you guys will greatly appreciated! Thank you
The idea to create mat is fine. I would not bother to strip off the first and last line, as in fact it will be easier to work with when you keep them. That way a row reference like i-1 will not go out of range when you are at a non-wall location.
I would also not store characters like w in there, but specific numbers, like -1 for wall, 0 for free. Also store 0 for "A" and "B". When encountering those two letters, you could store their coordinates in specific variables (e.g. rowA, colA, rowB, colB). You may need to check whether B is down-right from A, as otherwise B is certainly not reachable from A.
So I would define mat as follows (note that I reversed the dimensions, because your second example demonstrates that the first line of the input has them in that order):
int[][] mat = new int[Integer.valueOf(dimensions[1])]
[Integer.valueOf(dimensions[0])];
int colA = mat[0].length;
int rowA = 0;
int colB = colA;
int rowB = 0;
for (int i = 0; i < mat.length; i++) {
String currLine = lines.get(i+1);
int j = 0;
for (char c : currLine.toCharArray()) {
mat[i][j] = c == '*' ? -1 : 0;
if (c == 'B') {
if (colA > j) return 0; // B unreachable from A
rowB = i;
colB = j;
} else if (c == 'A') {
if (colB < j) return 0; // B unreachable from A
rowA = i;
colA = j;
}
j++;
}
}
With this setup you can reuse mat to store the number of paths from A to the current position. The value 0 at A should be set to 1 (there is one path from A to A), and then it is a matter of adding up the value from the cell above and left, making sure that -1 is treated as a 0.
mat[rowA][colA] = 1;
for (int i = rowA; i <= rowB; i++) {
for (int j = colA; j <= colB; j++) {
if (mat[i][j] == 0) { // not a wall?
// count the number of paths that come from above,
// plus the number of paths that come from the left
mat[i][j] = Math.max(0, mat[i-1][j]) + Math.max(0, mat[i][j-1]);
}
}
}
return mat[rowB][colB]; // now this has the number of paths we are looking for
Although a recursive method will also work, I would suggest the above dynamic programming approach, since that way you avoid to recalculate counts for a certain cell several times (when coming there via different DFS paths). This solution has a linear time complexity.
I propose a simple recursion with 2 calls: down and right.
This is the code:
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
public class JavaMazeInsideOfWallsAndGetAllPossiblePaths {
public static void main(String[] args) throws IOException, URISyntaxException {
Path mazePath = Paths.get( MethodHandles.lookup().lookupClass().getClassLoader()
.getResource("maze.txt").toURI());
File mazeFile = mazePath.toFile();
System.out.println(getPossiblePaths(mazeFile));
}
private static int getPossiblePaths(File f) throws IOException {
// read input file then put it on list string
List<String> lines = Files.lines(f.toPath()).collect(Collectors.toList());
// get the row and column (dimensions)
String[] dimensions = lines.get(0).split(",");
//initalize sub matrix of the maze dimensions and ignoring the top and bottom walls
int[][] mat = new int[Integer.valueOf(dimensions[0]) - 2 ][Integer.valueOf(dimensions[1]) - 2];
int fromRow = -1, fromCol = -1, toRow = -1, toCol = -1;
for( int i = 2 ; i < lines.size() - 1 ; i++) {
String currLine = lines.get(i);
int j = 0;
for(char c : currLine.toCharArray()) {
switch(c) {
case '*':
continue; // for loop
case 'A':
mat[i-2][j] = 0;
fromRow = i-2;
fromCol = j;
break;
case 'B':
mat[i-2][j] = 2;
toRow = i-2;
toCol = j;
break;
default:
mat[i-2][j] = 1;
}
j++;
}
}
return getPossiblePathsRecursive(mat, fromRow, fromCol, toRow, toCol);
}
private static int getPossiblePathsRecursive(int[][] mat, int i, int j, int rows, int columns) throws IOException {
if(i > rows || j > columns) {
return 0;
}
if(mat[i][j] == 2) {
return 1;
}
return getPossiblePathsRecursive(mat, i+1, j, rows, columns) +
getPossiblePathsRecursive(mat, i, j + 1, rows, columns);
}
}
Notes:
1. The validation step is skipped (assuming that the input data is in a valid format)
2. The walls are ignored (assuming that there are always 4 walls - first row, last row, first column, last column. These walls are assumed to be represented as '*')

Mix multiple strings into all the possible combinations

I have some strings.
1
2
3
How do I combine them into all their unique combinations?
123
132
213
231
312
321
Here is the code I have, but I would like to work without the Random class because I understand that this is not the best way to do it.
import java.util.Random;
public class Solution
{
public static void main(String[] args)
{
String[] names = new String[]{"string1", "string2", "string3"};
for (int i = 0; i < 9; i++) {
Random rand = new Random();
int rand1 = rand.nextInt(3);
System.out.println(names[rand.nextInt(3)] +
names[rand1] +
names[rand.nextInt(3)]);
}
}
}
You can loop over the array by creating another nested loop for each repetition.
for (String word1 : words) {
for (String word2 : words) {
for (String word3 : words) {
System.out.println(word1 + word2 + word3);
}
}
}
Here is how to avoid having the same word in one combination.
for (String word1 : words) {
for (String word2 : words) {
if ( !word1.equals(word2)) {
for (String word3 : words) {
if ( !word3.equals(word2) && !word3.equals(word1)) {
System.out.println(word1 + word2 + word3);
}
}
}
}
}
Here is a class version that is capable of multiple lengths, using backtracking.
import java.util.ArrayList;
import java.util.List;
public class PrintAllCombinations {
public void printAllCombinations() {
for (String combination : allCombinations(new String[] { "A", "B", "C" })) {
System.out.println(combination);
}
}
private List<String> allCombinations(final String[] values) {
return allCombinationsRecursive(values, 0, values.length - 1);
}
private List<String> allCombinationsRecursive(String[] values, final int i, final int n) {
List<String> result = new ArrayList<String>();
if (i == n) {
StringBuilder combinedString = new StringBuilder();
for (String value : values) {
combinedString.append(value);
}
result.add(combinedString.toString());
}
for (int j = i; j <= n; j++) {
values = swap(values, i, j);
result.addAll(allCombinationsRecursive(values, i + 1, n));
values = swap(values, i, j); // backtrack
}
return result;
}
private String[] swap(final String[] values, final int i, final int j) {
String tmp = values[i];
values[i] = values[j];
values[j] = tmp;
return values;
}
}
Please note that using the random method, it is never guaranteed that all combinations are being get. Therefore, it should always loop over all values.
You could use the Google Guava library to get all string permutations.
Collection<List<String>> permutations = Collections2.permutations(Lists.newArrayList("string1", "string2", "string3"));
for (List<String> permutation : permutations) {
String permutationString = Joiner.on("").join(permutation);
System.out.println(permutationString);
}
Output:
string1string2string3
string1string3string2
string3string1string2
string3string2string1
string2string3string1
string2string1string3
Firstly, there is nothing random in the result you are after - and Random.nextInt() will not give you unique permutations, or necessarily all permutations.
For N elements, there are N! (N-factorial) unique sequences - which I believe is what you are after. Therefore your three elements give six unique sequences (3! = 3 * 2 * 1).
This is because you have a choice of three elements for the first position (N), then a choice of the two remaining elements for the second position (N-1), leaving one unchosen element for the last position (N-2).
So, this means that you should be able to iterate over all permutations of the sequence; and the following code should do this for a sequence of 3 elements:
// Select element for first position in sequence...
for (int i = 0 ; i < 3 ; ++i)
{
// Select element for second position in sequence...
for (int j = 0 ; j < 3 ; ++j)
{
// step over indices already used - which means we
// must test the boundary condition again...
if (j >= i) ++j;
if (j >= 3) continue;
// Select element for third position in sequence...
// (there is only one choice!)
for (int k = 0 ; k < 3 ; ++k)
{
// step over indices already used, recheck boundary
// condition...
if (k >= i) ++k;
if (k >= j) ++k;
if (k >= 3) continue;
// Finally, i,j,k should be the next unique permutation...
doSomethingWith (i, j, k);
}
}
}
Now, big caveat that I have just written this OTH, so no guarentees. However, hopefully you can see what you need to do. Of course, this could and should be generalised to support arbitary set sizes, in which case you could populate an int[] with the indices for the sequence.
However, I guess that if you look around there will be some better algorithms for generating permutations of a sequence.

Integer to binary array

I'm trying to convert an integer to a 7 bit Boolean binary array. So far the code doesn't work:
If i input say integer 8 to be converted, instead of 0001000 I get 1000000, or say 15 I should get 0001111 but I get 1111000. The char array is a different length to the binary array and the positions are wrong.
public static void main(String[] args){
String maxAmpStr = Integer.toBinaryString(8);
char[] arr = maxAmpStr.toCharArray();
boolean[] binaryarray = new boolean[7];
for (int i=0; i<maxAmpStr.length(); i++){
if (arr[i] == '1'){
binaryarray[i] = true;
}
else if (arr[i] == '0'){
binaryarray[i] = false;
}
}
System.out.println(maxAmpStr);
System.out.println(binaryarray[0]);
System.out.println(binaryarray[1]);
System.out.println(binaryarray[2]);
System.out.println(binaryarray[3]);
System.out.println(binaryarray[4]);
System.out.println(binaryarray[5]);
System.out.println(binaryarray[6]);
}
Any help is appreciated.
There's really no need to deal with strings for this, just do bitwise comparisons for the 7 bits you're interested in.
public static void main(String[] args) {
int input = 15;
boolean[] bits = new boolean[7];
for (int i = 6; i >= 0; i--) {
bits[i] = (input & (1 << i)) != 0;
}
System.out.println(input + " = " + Arrays.toString(bits));
}
I would use this:
private static boolean[] toBinary(int number, int base) {
final boolean[] ret = new boolean[base];
for (int i = 0; i < base; i++) {
ret[base - 1 - i] = (1 << i & number) != 0;
}
return ret;
}
number 15 with base 7 will produce {false, false, false, true, true, true, true} = 0001111b
number 8, base 7 {false, false, false, true, false, false, false} = 0001000b
Hints: Think about what happens when you get a character representation that's less than seven characters.
In particular, think about how the char[] and boolean[] arrays "line up"; there will be extra elements in one than the other, so how should the indices coincide?
Actual answer: At the moment you're using the first element of the character array as the first element of the boolean array, which is only correct when you're using a seven-character string. In fact, you want the last elements of the arrays to coincide (so that the zeros are padded at the front not at the end).
One way to approach this problem would be to play around with the indices within the loop (e.g. work out the size difference and modify binaryarray[i + offset] instead). But an even simpler solution is just to left pad the string with zeros after the first line, to ensure it's exactly seven characters before converting it to the char array.
(Extra marks: what do you do when there's more than 7 characters in the array, e.g. if someone passes in 200 as an argument? Based on both solutions above you should be able to detect this case easily and handle it specifically.)
What you get when you do System.out.println(maxAmpStr); is "1000" in case of the 8.
So, you only get the relevant part, the first "0000" that you expected is just ommitted.
It's not pretty but what you could do is:
for (int i=0; i<maxAmpStr.length(); i++)
{
if (arr[i] == '1')
{
binaryarray[i+maxAmpStr.length()-1] = true;
}
else if (arr[i] == '0')
{
binaryarray[i+maxAmpStr.length()-1] = false;
}
}
Since nobody here has a answer with a dynamic array length, here is my solution:
public static boolean[] convertToBinary(int number) {
int binExpo = 0;
int bin = 1;
while(bin < number) { //calculates the needed digits
bin = bin*2;
binExpo++;
}
bin = bin/2;
boolean[] binary = new boolean[binExpo]; //array with the right length
binExpo--;
while(binExpo>=0) {
if(bin<=number) {
binary[binExpo] = true;
number =number -bin;
bin = bin/2;
}else {
binary[binExpo] = false;
}
binExpo--;
}
return binary;
}
The char-array is only as long as needed, so your boolean-array might be longer and places the bits at the wrong position. So start from behind, and when your char-array is finished, fill your boolean-array with 0's until first position.
Integer.toBinaryString(int i) does not pad. For e.g. Integer.toBinaryString(7) prints 111 not 00000111 as you expect. You need to take this into account when deciding where to start populating your boolean array.
15.ToBinaryString will be '1111'
You are lopping through that from first to last character, so the first '1' which is bit(3) is going into binaryArray[0] which I'm assuming should be bit 0.
You ned to pad ToBinaryString with leading zeros to a length of 7 (8 ??)
and then reverse the string, (or your loop)
Or you could stop messing about with strings and simply use bit wise operators
BinaryArray[3] = (SomeInt && 2^3 != 0);
^ = power operator or if not (1 << 3) or whatever is left shift in Java.
public static boolean[] convertToBinary(int b){
boolean[] binArray = new boolean[7];
boolean bin;
for(int i = 6; i >= 0; i--) {
if (b%2 == 1) bin = true;
else bin = false;
binArray[i] = bin;
b/=2;
}
return binArray;
}
public static String intToBinary(int num) {
int copy = num;
String sb = "";
for(int i=30; i>=0; i--) {
sb = (copy&1) + sb;
copy = copy >>>=1;
}
return sb;
}
AND the number with 1
Append the vale to a string
do unsigned right shift
repeat steps 1-3 for i=30..0
String maxAmpStr = Integer.toBinaryString(255);
char[] arr = maxAmpStr.toCharArray();
boolean[] binaryarray = new boolean[20];
int pivot = binaryarray.length - arr.length;
int j = binaryarray.length - 1;
for (int i = arr.length - 1; i >= 0; i--) {
if (arr[i] == '1') {
binaryarray[j] = true;
} else if (arr[i] == '0') {
binaryarray[j] = false;
}
if (j >= pivot)
j--;
}
System.out.println(maxAmpStr);
for (int k = 0; k < binaryarray.length; k++)
System.out.println(binaryarray[k]);
}

Why does my isFullHouse() method also accept a simple three-of-a-kind?

I am having problems with my full house method. I thought it was as simple as checking for three of a kind and a pair. But with my current code i am getting a full house with only a three of a kind. Code for isFullHouse() isThreeOfAKind() and isPair() is below thanks for all the help!
public boolean isPair() {
Pips[] values = new Pips[5];
int count =0;
//Put each cards numeric value into array
for(int i = 0; i < cards.length; i++){
values[i] = cards[i].getPip();
}
//Loop through the values. Compare each value to all values
//If exactly two matches are made - return true
for(int x = 1; x < values.length; x++){
for(int y = 0; y < x; y++){
if(values[x].equals(values[y])) count++;
}
if (count == 1) return true;
count = 0;
}
return false;
}
public boolean isThreeOfAKind() {
Pips[] values = new Pips[5];
int counter = 0;
for(int i = 0; i < cards.length; i++){
values[i] = cards[i].getPip();
}
//Same process as isPair(), except return true for 3 matches
for(int x = 2; x < values.length; x++){
for(int y = 0; y < x; y++){
if(values[x].equals(values[y]))
counter++;
}
if(counter == 2) return true;
counter = 0;
}
return false;
}
public boolean isFullHouse(){
if(isThreeOfAKind() && isPair())
return true;
return false;
}
Check to make sure that the pair is of a different rank than the three of a kind. Otherwise, your isPair() function will find the same cards as the three of a kind. Maybe like this:
public boolean isFullHouse(){
int three = isThreeOfAKind();
int pair = isPair();
if (three != 0 && pair != 0 && three != pair) {
return true;
}
return false;
}
(I used int, but you could change to use your Pips type if you like.)
Can I suggest a way of making your logic dramatically simpler?
Consider a helper method named partitionByRank():
public class RankSet {
private int count;
private Rank rank;
}
/**
* Groups the hand into counts of cards with same rank, sorting first by
* set size and then rank as secondary criteria
*/
public List<RankSet> partitionByRank() {
//input e.g.: {Kh, Qs, 4s, Kd, Qs}
//output e.g.: {[2, K], [2, Q], [1, 4]}
}
Getting the type of hand is really easy then:
public boolean isFullHouse() {
List<RankSet> sets = partitionByRank();
return sets.length() == 2 && sets.get(0).count == 3 && sets.get(1).count() == 2;
}
public boolean isTrips() {
//...
return sets.length() == 3 && sets.get(0).count = 3;
}
This will also help later on when you inevitably need to check whether one pair is greater than another pair, e.g.
You have to remove the three of a kind cards from the five card hand first. Three of a kind is true implies two of a kind is true. The sets need to be disjoint.
You are missing a third condition: the triple needs to be different cards than the pair. Soo... since you have this shared "cards" array, you probably could "mark" the cards as counted, and reset the counted status for each pass:
//Same process as isPair(), except return true for 3 matches
for(int x = 2; x < values.length; x++){
cards[x].setCounted(true); // by default, count the start card
for(int y = 0; y < x; y++){
// make sure the card isn't already counted:
if(!cards[y].isCounted() && values[x].equals(values[y])) {
counter++;
cards[x].setCounted(true); // count it
}
}
if(counter == 2) return true;
counter = 0;
// reset counted cards
for(int z=0, zlen=values.length; z < zlen; z++) { cards[z].setCounted(false); }
}
because three of a kind has a pair as well (actually would probably be 2 pairs in your code)
one way to do this is to sort the hand by rank, then its just conditionals to detect a boat.
if ( ((c1.rank == c2.rank == c3.rank) && (c4.rank == c5.rank)) ||
(c1.rank == c2.rank) && (c3.rank == c4.rank == c5.rank))
ther emight be an extra ( in there but you get the idea...
You need to make sure the pair is a different two cards than the three of a kind. If the hand is A A A 7 8, then both ThreeOfAKind and isPair return true because you have three aces (and a pair of aces).
Your isPair() method will always return true when there are three cards of a kind because your inner loop always tests the y values only up to x.
so with this data AAA78, when x = 1 y = 0 you will get count == 1 in the inner loop and return true although there are three of a kind. It's better to loop over the entire array and count values when
if(values[x].equals(values[y]) && x != y)
Besides - it's better to use one function in the form of isNOfAKind() which gets the amount of cards as a parameter since these two methods essentially do the same.
Just an idea, wouldn't it be easier to do something like this:
int[] count=new int[13];//size of all ranks
for (i=0;i<5;i++)
count[ card[i].rank ] ++;
So you will have for example: 0 0 0 0 0 3 0 0 0 2 0 0 0 0 for a full house. A straight would look like 5 ones in a row: 0 0 0 0 1 1 1 1 1 0 0 0.
Since the methods are public, I would not like the isPair() method to return true if there is a pair. It should only return true if there is nothing better than one pair.
A better general approach to the problem - this is C#, but converting it to Java should be straightforward:
int[] countOfRank = new int[13];
int[] countOfSuit = new int[4];
for(int i = 0; i < cards.length; i++)
{
countOfRank[cards[i].Rank]++;
countOfSuit[cards[i].Suit]++;
}
for (int i=0; i < countOfSuit.length; i++)
{
isFlush = isFlush || countOfSuit[i] == 5;
}
int[] countOfTuple = new int[5];
int runLength=0;
for (int i=0; i < countOfRank.length; i++)
{
if (countOfRank[i] == 1)
{
runLength++;
isStraight = (isStraight || runLength == 5);
}
else
{
runLength=0;
}
countOfTuple[countOfRank[i]]++;
}
isPair = (countOfTuple[2] == 1 && countOfTuple[3] == 0);
isTwoPair = (countOfTuple[2] == 2);
isFullHouse = (countOfTuple[2] == 1 && countOfTuple[3] == 1);
isThreeOfAKind = (countOfTuple[2] == 0 && countOfTuple[3] == 1);
isFourOfAKind = (countOfTuple[4] == 1);
isStraightFlush = (isStraight && isFlush);
isStraight = (isStraight && !isStraightFlush);
isFlush = (isFlush && !isStraightFlush);
isRoyalFlush = (isStraightFlush && countOfRank[12] == 1);
isStraightFlush = (isStraightFlush && !isRoyalFlush);
If you're only dealing with five-card hands, counting the number of pairs should yield one for a pair, two for two-pair, three for three-of-a-kind (e.g. if one has As, Ad, and Ac, the pairs are As-Ad, As-Ac, and Ad-Ac), four for a full house, and six for four-of-a-kind. This logic will not work with seven card hands, since it would count three for e.g. A-A-K-K-Q-Q-J (which should only count as two-pair, not three-of-a-kind), and six for A-A-A-K-K-K-Q (which should count as a full house, not four-of-a-kind).
According to your code inlined comments (exactly two matches words) maybe you are trying to implement isPair method in such a way that it will return false in case of three of a kind combination. If so, you need change your isPair method to iterate over all items in the array, like this:
//Loop through the values. Compare each value to all values
//If exactly two matches are made - return true
for(int x = 0; x < values.length; x++){
for(int y = 0; y < values.length; y++){
if(y != x && values[x].equals(values[y])) count++;
}
if (count == 1) return true;
count = 0;
}

Categories

Resources