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:
parent
606625d08b
commit
b875a66ab5
5 changed files with 67 additions and 69 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in a new issue