Reputation: 263
I the following assignment due in a few days in my AP Computer Science course:
"In this assignment you will model the game of Bulgarian Solitaire. The game starts with 45 cards. Randomly divide them into some number of piles of random size. For example, you might start with piles of size 20, 5, 1, 9, and 10. In each round you take one card from each pile, forming a new pile with these cards. For example, the sample starting configuration would be transformed into piles of size 19, 4, 8,10, and 5. The solitaire is over when the piles have size 1, 2, 3, 4, 5, 6, 7, 8, and 9, in some order.
In your program, produce a random starting configuration and print it. Then keep applying the solitaire step and print the result. Stop when the solitaire final configuration is reached."
I have come up with a program that solves this, but the problem is that it takes a VERY long time sometimes. Other times it will solve it almost instantly, as I expected, but other times it can 18,000 iterations or more.
According to http://mancala.wikia.com/wiki/Bulgarian_Solitaire the solution can be found in (k^2)-k steps or less, with k being 9 in this case. I'm definitely not finding the solution in 72 steps or less a lot of the time. I've been looking at this program for hours, messing with different things in order to see if I can make it faster and I just cannot get it working in a sufficient number of iterations. So now I've come to Stack Overflow to see if any of you can help to push me in the right direction.
Here is my code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class BulgarianSolitaire {
ArrayList<Integer> cards = new ArrayList<Integer>();
Random rand = new Random();
boolean cont = true;
boolean cont2 = true;
public static void main(String[] args) {
BulgarianSolitaire game = new BulgarianSolitaire();
}
public BulgarianSolitaire() {
System.out.println("Started");
int sum = 0;
while (cont) {
if (sum < 45) {
cards.add(rand.nextInt(46 - sum));
} else {
cont = false;
}
sum = 0;
for (int i = 0; i < cards.size(); i++) {
sum += cards.get(i);
}
removeZeros(cards);
System.out.println(cards);
}
System.out.println("Finished Generating Start");
while (cont2) {
solitaireStep();
System.out.println(cards);
if (checkCards()) {
cont2 = false;
}
}
Collections.sort(cards);
System.out.println("Cards are sorted");
System.out.println(cards);
}
public void removeZeros(ArrayList<Integer> list) {
for (int j = 0; j < list.size(); j++) {
if (list.get(j) == 0) {
list.remove(j);
}
}
}
public void solitaireStep() {
int numberRemoved = 0;
for (int i = 0; i < cards.size(); i++) {
int value = cards.get(i);
cards.set(i, value - 1);
removeZeros(cards);
numberRemoved++;
}
cards.add(numberRemoved);
}
public boolean checkCards() {
ArrayList<Integer> expectedCards = new ArrayList<Integer>();
for (int i = 1; i < 10; i++) {
expectedCards.add(i);
}
ArrayList<Integer> sortedCards = cards;
Collections.sort(sortedCards);
boolean equal = true;
if (sortedCards.size() != expectedCards.size()) {
equal = false;
}
for (int i = 0; i < sortedCards.size(); i++) {
if (sortedCards.size() == expectedCards.size()) {
if (sortedCards.get(i) != expectedCards.get(i)) {
equal = false;
}
}
}
return equal;
}
}
So I basically start by generating a random number between 0 and 45 and add that to the list of cards. Then I continue generating random numbers and putting them in the list as long as the sum is less than 45, such that the random numbers generated are between 0 and 45-the sum of the numbers in it in the last iteration. The zeros in the list are removed as it goes along as well.
Once the list has been generated, it will run through the step of subtracting 1 from every number in the list, removing zeros and adding a new value equal to the number of stacks that were decreased. It also checks an ordered version of the card stack against the list {1, 2, 3, 4, 5, 6, 7, 8, 9} and once it finds a match, it sets the boolean cont2 to false so that it will stop performing that solitaire step.
That's about it. I give my thanks to anyone that can help.
Upvotes: 2
Views: 8349
Reputation: 41
package learningofAlgorithm;
import java.util.ArrayList;
import java.util.Random;
import java.util.Collections;
public class bulgarianSolitaire {
private Random gen;
private int i=1;
ArrayList<Integer> bs=new ArrayList<>();
public bulgarianSolitaire(){
/**gen=new Random();
bs.add(gen.nextInt(45)+1);
int sum=bs.get(0);
while(sum!=45){
bs.add(gen.nextInt(45-sum)+1);
sum=sum+bs.get(i);
i++;
}*/
bs.add(20);
bs.add(5);
bs.add(1);
bs.add(9);
bs.add(10);
}
public void dis(){
for(Integer element:bs){
System.out.print(element+" ");
}
}
public void removeCard(ArrayList<Integer> s){
int i=0;
while(i<s.size()){
if(s.get(i)==0){
s.remove(i);
}
else{
i++;
}
}
}
public void bsstep(){
int newheap=0;
for(int i=0;i<bs.size();i++){
int key=bs.get(i);
bs.set(i, key-1);
newheap++;
}
removeCard(bs);
bs.add(newheap);
}
public boolean checkCard()
{ ArrayList<Integer> bscheck=new ArrayList<>();
for(int i=1;i<10;i++){
bscheck.add(i);
}
boolean flag=true;
ArrayList<Integer> sortbs=bs;
Collections.sort(sortbs);
if(bscheck.size()!=sortbs.size()){
flag=false;
}
for(int i=0;i<bscheck.size();i++){
if(bscheck.size()==sortbs.size()){
if(bscheck.get(i)!=sortbs.get(i)){
flag=false;
}
}
}
return flag;
}
}
Upvotes: 0
Reputation: 13
I have tried the code above but it did not work. I code a new one and it works:
import java.util.ArrayList;
import java.util.Collections;
public class P74_BulgarianSolitaire {
public static void main(String[] args) {
// TODO Auto-generated method stub
final int MAX_CARD = 45;
ArrayList<ArrayList<Integer>> twoDArrayList = new ArrayList<ArrayList<Integer>>();
int column = 1;
int card = 0;
int left = MAX_CARD;
do{
column = (int) (Math.random()*(left)+1); //
System.out.println("Column :"+column);
ArrayList<Integer> row = new ArrayList<Integer>(); //New row to add. Must declare here.
for (int j=0;j<column;j++){
card++;
row.add(card);
}
twoDArrayList.add(row);
left = MAX_CARD - card ;
} while (card <MAX_CARD);
System.out.println(twoDArrayList);
System.out.println();
boolean finish = false;
while (!finish){
ArrayList<Integer> row = new ArrayList<Integer>(); //New row
//remove one card from each row
for (int i=0;i<twoDArrayList.size();i++){
row.add(twoDArrayList.get(i).get(0));
twoDArrayList.get(i).remove(0); //Remove the first column
if (twoDArrayList.get(i).isEmpty()){
twoDArrayList.remove(twoDArrayList.get(i));
i--;
}
}
twoDArrayList.add(row);
ArrayList<Integer> size = new ArrayList<Integer>(); // New list
for (int i=0;i<twoDArrayList.size();i++){
size.add(twoDArrayList.get(i).size());
}
Collections.sort(size);
for (int i=1;i<size.size();i++){
if (size.get(i)== size.get(i-1)+1 ) finish = true;
else {
finish = false;
break;
}
}
for (int i=0;i<twoDArrayList.size();i++) Collections.sort(twoDArrayList.get(i));
System.out.println(twoDArrayList);
}
System.out.println(twoDArrayList);
}
}
Upvotes: 0
Reputation: 5131
Your flaw lies in your removeZeros
method.
public void removeZeros(ArrayList<Integer> list) {
for (int j = 0; j < list.size(); j++) {
if (list.get(j) == 0) {
list.remove(j);
}
}
}
If you remove the element at j
, then the list size decreases by 1. You have to decrease j
, too.
Change that for this:
public void removeZeros(ArrayList<Integer> list) {
for (int j = 0; j < list.size(); j++) {
if (list.get(j) == 0) {
list.remove(j);
j--;
}
}
}
Your checking method is also overly complicated.
In your solitaire step, set all of the values that should be equal to zero, to zero.
Then, remove zeros (with the revised method) outside of the loop.
Then, sort the array.
Then, in your checking method, since the array is sorted:
public boolean checkCards() {
for(int i = 0; i < cards.size(); i++) {
if(cards.get(i) != i + 1) {
return false;
}
}
return true;
}
Much simpler. And it works.
Upvotes: 2