Generator now works for every Difficulty and GameType. I had to change the generator for the 12x12 Generation to leave more clues because it had a very hard time generating levels for the "easy" difficulty level.

It's not very pretty but it works in a reasonable time. Before it would just turn black and stay that way for half an hour.
This commit is contained in:
Christopher Beckmann 2015-11-22 17:45:17 +01:00
parent 606625d08b
commit b875a66ab5
5 changed files with 67 additions and 69 deletions

View file

@ -20,8 +20,6 @@ import tu_darmstadt.sudoku.game.GameType;
*/
public class QQWingController {
private static final String NL = System.getProperties().getProperty("line.separator");
final QQWingOptions opts = new QQWingOptions();
private int[] level;
@ -30,11 +28,12 @@ public class QQWingController {
private boolean solveImpossible = false;
public int[] generate(GameType type, GameDifficulty difficulty) {
// TODO: GameType options.
opts.gameDifficulty = difficulty;
opts.action = Action.GENERATE;
opts.printSolution = false;
opts.threads = Runtime.getRuntime().availableProcessors();
doAction(type);
opts.gameType = type;
doAction();
return generated;
}
@ -54,7 +53,8 @@ public class QQWingController {
opts.action = Action.SOLVE;
opts.printSolution = true;
opts.threads = 1;
doAction(gameBoard.getGameType());
opts.gameType = gameBoard.getGameType();
doAction();
if(solveImpossible) {
// TODO: do something else.
@ -62,11 +62,10 @@ public class QQWingController {
return solution;
}
private void doAction(final GameType gameType) {
private void doAction() {
// The number of puzzles solved or generated.
final AtomicInteger puzzleCount = new AtomicInteger(0);
final AtomicBoolean done = new AtomicBoolean(false);
final AtomicIntegerArray result = new AtomicIntegerArray(new int[gameType.getSize()*gameType.getSize()]);
Thread[] threads = new Thread[opts.threads];
for (int threadCount = 0; threadCount < threads.length; threadCount++) {
@ -78,7 +77,7 @@ public class QQWingController {
private QQWing ss = createQQWing();
private QQWing createQQWing() {
QQWing ss = new QQWing(gameType);
QQWing ss = new QQWing(opts.gameType, opts.gameDifficulty);
ss.setRecordHistory(opts.printHistory || opts.printInstructions || opts.printStats || opts.gameDifficulty != GameDifficulty.Unspecified);
ss.setLogHistory(opts.logHistory);
ss.setPrintStyle(opts.printStyle);
@ -94,15 +93,11 @@ public class QQWingController {
// until we have generated the specified number.
while (!done.get()) {
// iff something has been printed for this
// particular puzzle
StringBuilder output = new StringBuilder();
// Record whether the puzzle was possible or
// not,
// so that we don't try to solve impossible
// givens.
boolean havePuzzle = false;
boolean havePuzzle;
if (opts.action == Action.GENERATE) {
// Generate a puzzle
@ -135,9 +130,9 @@ public class QQWingController {
// Count the solutions if requested.
// (Must be done before solving, as it would
// mess up the stats.)
if (opts.countSolutions) {
solutions = ss.countSolutions();
}
//if (opts.countSolutions) {
// solutions = ss.countSolutions();
//}
// Solve the puzzle
if (opts.printSolution || opts.printHistory || opts.printStats || opts.printInstructions || opts.gameDifficulty != GameDifficulty.Unspecified) {
@ -148,23 +143,10 @@ public class QQWingController {
// Bail out if it didn't meet the gameDifficulty
// standards for generation
if (opts.action == Action.GENERATE) {
if (opts.gameDifficulty != GameDifficulty.Unspecified && opts.gameDifficulty != ss.getDifficulty()) {
havePuzzle = false;
// check if other threads have
// finished the job
if (puzzleCount.get() >= opts.numberToGenerate) {
if (opts.gameDifficulty != GameDifficulty.Unspecified && opts.gameDifficulty == ss.getDifficulty()) {
done.set(true);
generated = ss.getPuzzle();
}
} else {
int numDone = puzzleCount.incrementAndGet();
if (numDone >= opts.numberToGenerate) {
done.set(true);
generated = ss.getPuzzle();
}
if (numDone > opts.numberToGenerate)
havePuzzle = false;
}
}
}
}
@ -202,6 +184,7 @@ public class QQWingController {
int numberToGenerate = 1;
boolean printStats = false;
GameDifficulty gameDifficulty = GameDifficulty.Unspecified;
GameType gameType = GameType.Unspecified;
Symmetry symmetry = Symmetry.NONE;
int threads = Runtime.getRuntime().availableProcessors();
}

View file

@ -132,10 +132,16 @@ public class QQWing {
*/
private PrintStyle printStyle = PrintStyle.READABLE;
private GameType gameType = GameType.Unspecified;
private GameDifficulty difficulty = GameDifficulty.Unspecified;
/**
* Create a new Sudoku board
*/
public QQWing(GameType type) {
public QQWing(GameType type, GameDifficulty difficulty) {
gameType = type;
this.difficulty = difficulty;
GRID_SIZE_ROW = type.getSectionHeight(); // 3 // 2
GRID_SIZE_COL = type.getSectionWidth(); // 3 // 3
@ -221,13 +227,21 @@ public class QQWing {
* Get the gameDifficulty rating.
*/
public GameDifficulty getDifficulty() {
if (getGuessCount() > 0) return GameDifficulty.Hard;
if (getBoxLineReductionCount() > 0) return GameDifficulty.Moderate;
if (getPointingPairTripleCount() > 0) return GameDifficulty.Moderate;
if (getHiddenPairCount() > 0) return GameDifficulty.Moderate;
if (getNakedPairCount() > 0) return GameDifficulty.Moderate;
if (getHiddenSingleCount() > 0) return GameDifficulty.Easy;
if (getSingleCount() > 0) return GameDifficulty.Easy;
if (getGuessCount() > 0)
return GameDifficulty.Hard;
if (getBoxLineReductionCount() > 0)
return GameDifficulty.Moderate;
if (getPointingPairTripleCount() > 0)
return GameDifficulty.Moderate;
if (getHiddenPairCount() > 0)
return GameDifficulty.Moderate;
if (getNakedPairCount() > 0)
return GameDifficulty.Moderate;
if (getHiddenSingleCount() > 0)
return GameDifficulty.Easy;
if (getSingleCount() > 0)
return GameDifficulty.Easy;
return GameDifficulty.Unspecified;
}
@ -434,6 +448,11 @@ public class QQWing {
// Non-guesses are even rounds
for (int i = 2; i <= lastSolveRound; i += 2) {
rollbackRound(i);
// Some hack to make easy levels on 12x12 .. because the generator wasn't able to create some
if(gameType == GameType.Default_12x12 && difficulty == GameDifficulty.Easy ) {
i += 4; // skip every 2nd round
}
}
}
@ -667,7 +686,7 @@ public class QQWing {
}
private int findPositionWithFewestPossibilities() {
int minPossibilities = 10;
int minPossibilities = ROW_COL_SEC_SIZE+1;
int bestPosition = 0;
for (int i = 0; i < BOARD_SIZE; i++) {
int position = randomBoardArray[i];

View file

@ -46,7 +46,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
super.onCreate(savedInstanceState);
GameType gameType = GameType.Unspecified;
int gameDifficulty = 0;
GameDifficulty gameDifficulty = GameDifficulty.Unspecified;
int loadLevelID = 0;
boolean loadLevel = false;
@ -56,7 +56,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
if(o instanceof GameType) {
gameType = (GameType)extras.get("gameType");
}
gameDifficulty = extras.getInt("gameDifficulty");
gameDifficulty = (GameDifficulty)(extras.get("gameDifficulty"));
loadLevel = extras.getBoolean("loadLevel");
if(loadLevel) {
loadLevelID = extras.getInt("loadLevelID");
@ -83,7 +83,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
gameController.loadLevel(loadableGames.get(loadLevelID));
} else {
// load a new level
gameController.loadNewLevel(gameType, GameDifficulty.getValidDifficultyList().get(gameDifficulty));
gameController.loadNewLevel(gameType, gameDifficulty);
}
layout.setGame(gameController);

View file

@ -12,7 +12,6 @@ import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@ -23,8 +22,6 @@ import android.widget.TextView;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import tu_darmstadt.sudoku.controller.SaveLoadController;
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
@ -37,17 +34,6 @@ public class MainActivity extends AppCompatActivity {
RatingBar difficultyBar;
TextView difficultyText;
SharedPreferences settings;
Timer t = new Timer();
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {@link ViewPager} that will host the section contents.
@ -67,7 +53,15 @@ public class MainActivity extends AppCompatActivity {
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
/*
The {@link android.support.v4.view.PagerAdapter} that will provide
fragments for each of the sections. We use a
{@link FragmentPagerAdapter} derivative, which will keep every
loaded fragment in memory. If this becomes too memory intensive, it
may be best to switch to a
{@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
@ -95,13 +89,13 @@ public class MainActivity extends AppCompatActivity {
difficultyText.setText(getString(difficultyList.get((int) ratingBar.getRating() - 1).getStringResID()));
}
});
int lastChosenDifficulty = settings.getInt("lastChosenDifficulty", 1);
difficultyBar.setRating(lastChosenDifficulty);
GameDifficulty lastChosenDifficulty = GameDifficulty.valueOf(settings.getString("lastChosenDifficulty", "Easy"));
difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(lastChosenDifficulty)+1);
// on first create always check for loadable levels!
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("savesChanged", true);
editor.commit();
editor.apply();
refreshContinueButton();
}
@ -129,13 +123,14 @@ public class MainActivity extends AppCompatActivity {
break;
case R.id.playButton:
GameType gameType = GameType.getValidGameTypes().get(mViewPager.getCurrentItem());
int gameDifficulty = difficultyBar.getProgress()-1;
int index = difficultyBar.getProgress()-1;
GameDifficulty gameDifficulty = GameDifficulty.getValidDifficultyList().get(index < 0 ? 0 : index);
// save current setting for later
SharedPreferences.Editor editor = settings.edit();
editor.putString("lastChosenGameType", gameType.name());
editor.putInt("lastChosenDifficulty", gameDifficulty);
editor.commit();
editor.putString("lastChosenDifficulty", gameDifficulty.name());
editor.apply();
// send everything to game activity
i = new Intent(this, GameActivity.class);
@ -169,7 +164,7 @@ public class MainActivity extends AppCompatActivity {
}
}
@Override
/*@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
@ -177,7 +172,7 @@ public class MainActivity extends AppCompatActivity {
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
}*/
/**

View file

@ -43,6 +43,7 @@
<string name="about_author_names">Christopher Beckmann, Timm Lippert</string>
<string name="about_affiliation">In affiliation with:</string>
<string name="privacy_friendly">This application belongs to the Privacy Friendly Apps.</string>
<string name="qqwing">This application uses a modified version of QQWing v1.3.4</string>
<string name="more_info">More information can be found on:</string>
<string name="url"><a href="https://www.secuso.informatik.tu-darmstadt.de/en/research/results/">https://www.secuso.org</a></string>