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 {
|
public class QQWingController {
|
||||||
|
|
||||||
private static final String NL = System.getProperties().getProperty("line.separator");
|
|
||||||
|
|
||||||
final QQWingOptions opts = new QQWingOptions();
|
final QQWingOptions opts = new QQWingOptions();
|
||||||
|
|
||||||
private int[] level;
|
private int[] level;
|
||||||
|
@ -30,11 +28,12 @@ public class QQWingController {
|
||||||
private boolean solveImpossible = false;
|
private boolean solveImpossible = false;
|
||||||
|
|
||||||
public int[] generate(GameType type, GameDifficulty difficulty) {
|
public int[] generate(GameType type, GameDifficulty difficulty) {
|
||||||
// TODO: GameType options.
|
|
||||||
opts.gameDifficulty = difficulty;
|
opts.gameDifficulty = difficulty;
|
||||||
opts.action = Action.GENERATE;
|
opts.action = Action.GENERATE;
|
||||||
|
opts.printSolution = false;
|
||||||
opts.threads = Runtime.getRuntime().availableProcessors();
|
opts.threads = Runtime.getRuntime().availableProcessors();
|
||||||
doAction(type);
|
opts.gameType = type;
|
||||||
|
doAction();
|
||||||
return generated;
|
return generated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,8 @@ public class QQWingController {
|
||||||
opts.action = Action.SOLVE;
|
opts.action = Action.SOLVE;
|
||||||
opts.printSolution = true;
|
opts.printSolution = true;
|
||||||
opts.threads = 1;
|
opts.threads = 1;
|
||||||
doAction(gameBoard.getGameType());
|
opts.gameType = gameBoard.getGameType();
|
||||||
|
doAction();
|
||||||
if(solveImpossible) {
|
if(solveImpossible) {
|
||||||
// TODO: do something else.
|
// TODO: do something else.
|
||||||
|
|
||||||
|
@ -62,11 +62,10 @@ public class QQWingController {
|
||||||
return solution;
|
return solution;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doAction(final GameType gameType) {
|
private void doAction() {
|
||||||
// The number of puzzles solved or generated.
|
// The number of puzzles solved or generated.
|
||||||
final AtomicInteger puzzleCount = new AtomicInteger(0);
|
final AtomicInteger puzzleCount = new AtomicInteger(0);
|
||||||
final AtomicBoolean done = new AtomicBoolean(false);
|
final AtomicBoolean done = new AtomicBoolean(false);
|
||||||
final AtomicIntegerArray result = new AtomicIntegerArray(new int[gameType.getSize()*gameType.getSize()]);
|
|
||||||
|
|
||||||
Thread[] threads = new Thread[opts.threads];
|
Thread[] threads = new Thread[opts.threads];
|
||||||
for (int threadCount = 0; threadCount < threads.length; threadCount++) {
|
for (int threadCount = 0; threadCount < threads.length; threadCount++) {
|
||||||
|
@ -78,7 +77,7 @@ public class QQWingController {
|
||||||
private QQWing ss = createQQWing();
|
private QQWing ss = createQQWing();
|
||||||
|
|
||||||
private QQWing 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.setRecordHistory(opts.printHistory || opts.printInstructions || opts.printStats || opts.gameDifficulty != GameDifficulty.Unspecified);
|
||||||
ss.setLogHistory(opts.logHistory);
|
ss.setLogHistory(opts.logHistory);
|
||||||
ss.setPrintStyle(opts.printStyle);
|
ss.setPrintStyle(opts.printStyle);
|
||||||
|
@ -94,15 +93,11 @@ public class QQWingController {
|
||||||
// until we have generated the specified number.
|
// until we have generated the specified number.
|
||||||
while (!done.get()) {
|
while (!done.get()) {
|
||||||
|
|
||||||
// iff something has been printed for this
|
|
||||||
// particular puzzle
|
|
||||||
StringBuilder output = new StringBuilder();
|
|
||||||
|
|
||||||
// Record whether the puzzle was possible or
|
// Record whether the puzzle was possible or
|
||||||
// not,
|
// not,
|
||||||
// so that we don't try to solve impossible
|
// so that we don't try to solve impossible
|
||||||
// givens.
|
// givens.
|
||||||
boolean havePuzzle = false;
|
boolean havePuzzle;
|
||||||
|
|
||||||
if (opts.action == Action.GENERATE) {
|
if (opts.action == Action.GENERATE) {
|
||||||
// Generate a puzzle
|
// Generate a puzzle
|
||||||
|
@ -135,9 +130,9 @@ public class QQWingController {
|
||||||
// Count the solutions if requested.
|
// Count the solutions if requested.
|
||||||
// (Must be done before solving, as it would
|
// (Must be done before solving, as it would
|
||||||
// mess up the stats.)
|
// mess up the stats.)
|
||||||
if (opts.countSolutions) {
|
//if (opts.countSolutions) {
|
||||||
solutions = ss.countSolutions();
|
// solutions = ss.countSolutions();
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Solve the puzzle
|
// Solve the puzzle
|
||||||
if (opts.printSolution || opts.printHistory || opts.printStats || opts.printInstructions || opts.gameDifficulty != GameDifficulty.Unspecified) {
|
if (opts.printSolution || opts.printHistory || opts.printStats || opts.printInstructions || opts.gameDifficulty != GameDifficulty.Unspecified) {
|
||||||
|
@ -148,22 +143,9 @@ public class QQWingController {
|
||||||
// Bail out if it didn't meet the gameDifficulty
|
// Bail out if it didn't meet the gameDifficulty
|
||||||
// standards for generation
|
// standards for generation
|
||||||
if (opts.action == Action.GENERATE) {
|
if (opts.action == Action.GENERATE) {
|
||||||
if (opts.gameDifficulty != GameDifficulty.Unspecified && opts.gameDifficulty != ss.getDifficulty()) {
|
if (opts.gameDifficulty != GameDifficulty.Unspecified && opts.gameDifficulty == ss.getDifficulty()) {
|
||||||
havePuzzle = false;
|
done.set(true);
|
||||||
// check if other threads have
|
generated = ss.getPuzzle();
|
||||||
// finished the job
|
|
||||||
if (puzzleCount.get() >= opts.numberToGenerate) {
|
|
||||||
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;
|
int numberToGenerate = 1;
|
||||||
boolean printStats = false;
|
boolean printStats = false;
|
||||||
GameDifficulty gameDifficulty = GameDifficulty.Unspecified;
|
GameDifficulty gameDifficulty = GameDifficulty.Unspecified;
|
||||||
|
GameType gameType = GameType.Unspecified;
|
||||||
Symmetry symmetry = Symmetry.NONE;
|
Symmetry symmetry = Symmetry.NONE;
|
||||||
int threads = Runtime.getRuntime().availableProcessors();
|
int threads = Runtime.getRuntime().availableProcessors();
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,10 +132,16 @@ public class QQWing {
|
||||||
*/
|
*/
|
||||||
private PrintStyle printStyle = PrintStyle.READABLE;
|
private PrintStyle printStyle = PrintStyle.READABLE;
|
||||||
|
|
||||||
|
private GameType gameType = GameType.Unspecified;
|
||||||
|
private GameDifficulty difficulty = GameDifficulty.Unspecified;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Sudoku board
|
* 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_ROW = type.getSectionHeight(); // 3 // 2
|
||||||
|
|
||||||
GRID_SIZE_COL = type.getSectionWidth(); // 3 // 3
|
GRID_SIZE_COL = type.getSectionWidth(); // 3 // 3
|
||||||
|
@ -221,13 +227,21 @@ public class QQWing {
|
||||||
* Get the gameDifficulty rating.
|
* Get the gameDifficulty rating.
|
||||||
*/
|
*/
|
||||||
public GameDifficulty getDifficulty() {
|
public GameDifficulty getDifficulty() {
|
||||||
if (getGuessCount() > 0) return GameDifficulty.Hard;
|
if (getGuessCount() > 0)
|
||||||
if (getBoxLineReductionCount() > 0) return GameDifficulty.Moderate;
|
return GameDifficulty.Hard;
|
||||||
if (getPointingPairTripleCount() > 0) return GameDifficulty.Moderate;
|
if (getBoxLineReductionCount() > 0)
|
||||||
if (getHiddenPairCount() > 0) return GameDifficulty.Moderate;
|
return GameDifficulty.Moderate;
|
||||||
if (getNakedPairCount() > 0) return GameDifficulty.Moderate;
|
if (getPointingPairTripleCount() > 0)
|
||||||
if (getHiddenSingleCount() > 0) return GameDifficulty.Easy;
|
return GameDifficulty.Moderate;
|
||||||
if (getSingleCount() > 0) return GameDifficulty.Easy;
|
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;
|
return GameDifficulty.Unspecified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,7 +448,12 @@ public class QQWing {
|
||||||
// Non-guesses are even rounds
|
// Non-guesses are even rounds
|
||||||
for (int i = 2; i <= lastSolveRound; i += 2) {
|
for (int i = 2; i <= lastSolveRound; i += 2) {
|
||||||
rollbackRound(i);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrintStyle(PrintStyle ps) {
|
public void setPrintStyle(PrintStyle ps) {
|
||||||
|
@ -667,7 +686,7 @@ public class QQWing {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findPositionWithFewestPossibilities() {
|
private int findPositionWithFewestPossibilities() {
|
||||||
int minPossibilities = 10;
|
int minPossibilities = ROW_COL_SEC_SIZE+1;
|
||||||
int bestPosition = 0;
|
int bestPosition = 0;
|
||||||
for (int i = 0; i < BOARD_SIZE; i++) {
|
for (int i = 0; i < BOARD_SIZE; i++) {
|
||||||
int position = randomBoardArray[i];
|
int position = randomBoardArray[i];
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
GameType gameType = GameType.Unspecified;
|
GameType gameType = GameType.Unspecified;
|
||||||
int gameDifficulty = 0;
|
GameDifficulty gameDifficulty = GameDifficulty.Unspecified;
|
||||||
int loadLevelID = 0;
|
int loadLevelID = 0;
|
||||||
boolean loadLevel = false;
|
boolean loadLevel = false;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
|
||||||
if(o instanceof GameType) {
|
if(o instanceof GameType) {
|
||||||
gameType = (GameType)extras.get("gameType");
|
gameType = (GameType)extras.get("gameType");
|
||||||
}
|
}
|
||||||
gameDifficulty = extras.getInt("gameDifficulty");
|
gameDifficulty = (GameDifficulty)(extras.get("gameDifficulty"));
|
||||||
loadLevel = extras.getBoolean("loadLevel");
|
loadLevel = extras.getBoolean("loadLevel");
|
||||||
if(loadLevel) {
|
if(loadLevel) {
|
||||||
loadLevelID = extras.getInt("loadLevelID");
|
loadLevelID = extras.getInt("loadLevelID");
|
||||||
|
@ -83,7 +83,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
|
||||||
gameController.loadLevel(loadableGames.get(loadLevelID));
|
gameController.loadLevel(loadableGames.get(loadLevelID));
|
||||||
} else {
|
} else {
|
||||||
// load a new level
|
// load a new level
|
||||||
gameController.loadNewLevel(gameType, GameDifficulty.getValidDifficultyList().get(gameDifficulty));
|
gameController.loadNewLevel(gameType, gameDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.setGame(gameController);
|
layout.setGame(gameController);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import android.support.v4.app.FragmentPagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
@ -23,8 +22,6 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import tu_darmstadt.sudoku.controller.SaveLoadController;
|
import tu_darmstadt.sudoku.controller.SaveLoadController;
|
||||||
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
|
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
|
||||||
|
@ -37,17 +34,6 @@ public class MainActivity extends AppCompatActivity {
|
||||||
RatingBar difficultyBar;
|
RatingBar difficultyBar;
|
||||||
TextView difficultyText;
|
TextView difficultyText;
|
||||||
SharedPreferences settings;
|
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.
|
* 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
|
// Create the adapter that will return a fragment for each of the three
|
||||||
// primary sections of the activity.
|
// 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.
|
// Set up the ViewPager with the sections adapter.
|
||||||
|
@ -92,16 +86,16 @@ public class MainActivity extends AppCompatActivity {
|
||||||
if (rating < 1) {
|
if (rating < 1) {
|
||||||
ratingBar.setRating(1);
|
ratingBar.setRating(1);
|
||||||
}
|
}
|
||||||
difficultyText.setText(getString(difficultyList.get((int)ratingBar.getRating()-1).getStringResID()));
|
difficultyText.setText(getString(difficultyList.get((int) ratingBar.getRating() - 1).getStringResID()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
int lastChosenDifficulty = settings.getInt("lastChosenDifficulty", 1);
|
GameDifficulty lastChosenDifficulty = GameDifficulty.valueOf(settings.getString("lastChosenDifficulty", "Easy"));
|
||||||
difficultyBar.setRating(lastChosenDifficulty);
|
difficultyBar.setRating(GameDifficulty.getValidDifficultyList().indexOf(lastChosenDifficulty)+1);
|
||||||
|
|
||||||
// on first create always check for loadable levels!
|
// on first create always check for loadable levels!
|
||||||
SharedPreferences.Editor editor = settings.edit();
|
SharedPreferences.Editor editor = settings.edit();
|
||||||
editor.putBoolean("savesChanged", true);
|
editor.putBoolean("savesChanged", true);
|
||||||
editor.commit();
|
editor.apply();
|
||||||
refreshContinueButton();
|
refreshContinueButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +123,14 @@ public class MainActivity extends AppCompatActivity {
|
||||||
break;
|
break;
|
||||||
case R.id.playButton:
|
case R.id.playButton:
|
||||||
GameType gameType = GameType.getValidGameTypes().get(mViewPager.getCurrentItem());
|
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
|
// save current setting for later
|
||||||
SharedPreferences.Editor editor = settings.edit();
|
SharedPreferences.Editor editor = settings.edit();
|
||||||
editor.putString("lastChosenGameType", gameType.name());
|
editor.putString("lastChosenGameType", gameType.name());
|
||||||
editor.putInt("lastChosenDifficulty", gameDifficulty);
|
editor.putString("lastChosenDifficulty", gameDifficulty.name());
|
||||||
editor.commit();
|
editor.apply();
|
||||||
|
|
||||||
// send everything to game activity
|
// send everything to game activity
|
||||||
i = new Intent(this, GameActivity.class);
|
i = new Intent(this, GameActivity.class);
|
||||||
|
@ -169,7 +164,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/*@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
// Handle action bar item clicks here. The action bar will
|
// Handle action bar item clicks here. The action bar will
|
||||||
// automatically handle clicks on the Home/Up button, so long
|
// automatically handle clicks on the Home/Up button, so long
|
||||||
|
@ -177,7 +172,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<string name="about_author_names">Christopher Beckmann, Timm Lippert</string>
|
<string name="about_author_names">Christopher Beckmann, Timm Lippert</string>
|
||||||
<string name="about_affiliation">In affiliation with:</string>
|
<string name="about_affiliation">In affiliation with:</string>
|
||||||
<string name="privacy_friendly">This application belongs to the Privacy Friendly Apps.</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="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>
|
<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