einstein/puzgen.cpp

395 lines
9.6 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <list>
#include "puzgen.h"
#include "exceptions.h"
#include "utils.h"
Possibilities::Possibilities()
{
reset();
}
Possibilities::Possibilities(std::istream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
for (int element = 0; element < PUZZLE_SIZE; element++)
pos[col][row][element] = readInt(stream);
}
void Possibilities::reset()
{
for (int i = 0; i < PUZZLE_SIZE; i++)
for (int j = 0; j < PUZZLE_SIZE; j++)
for (int k = 0; k < PUZZLE_SIZE; k++)
pos[i][j][k] = k + 1;
}
void Possibilities::checkSingles(int row)
{
int cellsCnt[PUZZLE_SIZE]; // count of elements in cells
int elsCnt[PUZZLE_SIZE]; // total count of elements in row
int elements[PUZZLE_SIZE]; // one element of each cell
int elCells[PUZZLE_SIZE]; // one cell of each element
memset(cellsCnt, 0, sizeof(cellsCnt));
memset(elsCnt, 0, sizeof(elsCnt));
memset(elements, 0, sizeof(elements));
memset(elCells, 0, sizeof(elCells));
// check if there is only one element left in cell(col, row)
for (int col = 0; col < PUZZLE_SIZE; col++)
for (int i = 0; i < PUZZLE_SIZE; i++) {
if (pos[col][row][i]) {
elsCnt[i]++;
elCells[i] = col;
cellsCnt[col]++;
elements[col] = i + 1;
}
}
bool changed = false;
// check for cells with single element
for (int col = 0; col < PUZZLE_SIZE; col++) {
if ((cellsCnt[col] == 1) && (elsCnt[elements[col] - 1] != 1)) {
// there is only one element in cell but it used somewhere else
int e = elements[col] - 1;
for (int i = 0; i < PUZZLE_SIZE; i++)
if (i != col)
pos[i][row][e] = 0;
changed = true;
}
}
// check for single element without exclusive cell
for (int el = 0; el < PUZZLE_SIZE; el++)
if ((elsCnt[el] == 1) && (cellsCnt[elCells[el]] != 1)) {
int col = elCells[el];
for (int i = 0; i < PUZZLE_SIZE; i++)
if (i != el)
pos[col][row][i] = 0;
changed = true;
}
if (changed)
checkSingles(row);
}
void Possibilities::exclude(int col, int row, int element)
{
if (! pos[col][row][element - 1])
return;
pos[col][row][element - 1] = 0;
checkSingles(row);
}
void Possibilities::set(int col, int row, int element)
{
for (int i = 0; i < PUZZLE_SIZE; i++)
if ((i != element - 1))
pos[col][row][i] = 0;
else
pos[col][row][i] = element;
for (int j = 0; j < PUZZLE_SIZE; j++)
if (j != col)
pos[j][row][element - 1] = 0;
checkSingles(row);
}
bool Possibilities::isPossible(int col, int row, int element)
{
return pos[col][row][element - 1] == element;
}
bool Possibilities::isDefined(int col, int row)
{
int solvedCnt = 0, unsolvedCnt = 0;
for (int i = 0; i < PUZZLE_SIZE; i++)
if (! pos[col][row][i])
unsolvedCnt++;
else
solvedCnt++;
return ((unsolvedCnt == PUZZLE_SIZE-1) && (solvedCnt == 1));
}
int Possibilities::getDefined(int col, int row)
{
for (int i = 0; i < PUZZLE_SIZE; i++)
if (pos[col][row][i])
return i + 1;
return 0;
}
bool Possibilities::isSolved()
{
for (int i = 0; i < PUZZLE_SIZE; i++)
for (int j = 0; j < PUZZLE_SIZE; j++)
if (! isDefined(i, j))
return false;
return true;
}
bool Possibilities::isValid(SolvedPuzzle &puzzle)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
if (! isPossible(col, row, puzzle[row][col]))
return false;
return true;
}
int Possibilities::getPosition(int row, int element)
{
int cnt = 0;
int lastPos = -1;
for (int i = 0; i < PUZZLE_SIZE; i++)
if (pos[i][row][element - 1] == element) {
cnt++;
lastPos = i;
}
return cnt == 1 ? lastPos : -1;
}
void Possibilities::print()
{
for (int row = 0; row < PUZZLE_SIZE; row++) {
std::cout << (char)('A' + row) << " ";
for (int col = 0; col < PUZZLE_SIZE; col++) {
for (int i = 0; i < PUZZLE_SIZE; i++)
if (pos[col][row][i])
std::cout << pos[col][row][i];
else
std::cout << " ";
std::cout << " ";
}
std::cout << std::endl;
}
}
void Possibilities::makePossible(int col, int row, int element)
{
pos[col][row][element-1] = element;
}
void Possibilities::save(std::ostream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
for (int element = 0; element < PUZZLE_SIZE; element++)
writeInt(stream, pos[col][row][element]);
}
static void shuffle(short arr[PUZZLE_SIZE])
{
int a, b, c;
for (int i = 0; i < 30; i++) {
a = (int)(((double)PUZZLE_SIZE)*rand()/(RAND_MAX+1.0));
if ((a < 0) || (a >= PUZZLE_SIZE)) {
std::cerr << "Index error" << std::endl;
exit(1);
}
b = (int)(((double)PUZZLE_SIZE)*rand()/(RAND_MAX+1.0));
if ((b < 0) || (b >= PUZZLE_SIZE)) {
std::cerr << "Index error" << std::endl;
exit(1);
}
c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
}
static bool canSolve(SolvedPuzzle &puzzle, Rules &rules)
{
Possibilities pos;
bool changed = false;
do {
changed = false;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule *rule = *i;
if (rule->apply(pos)) {
changed = true;
if (! pos.isValid(puzzle)) {
std::cout << "after error:" << std::endl;
pos.print();
throw Exception(L"Invalid possibilities after rule " +
rule->getAsText());
}
}
}
} while (changed);
bool res = pos.isSolved();
return res;
}
static void removeRules(SolvedPuzzle &puzzle, Rules &rules)
{
bool possible;
do {
possible = false;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule *rule = *i;
Rules excludedRules = rules;
excludedRules.remove(rule);
if (canSolve(puzzle, excludedRules)) {
possible = true;
rules.remove(rule);
delete rule;
break;
}
}
} while (possible);
}
static void genRules(SolvedPuzzle &puzzle, Rules &rules)
{
bool rulesDone = false;
do {
Rule *rule = genRule(puzzle);
if (rule) {
std::wstring s = rule->getAsText();
for (std::list<Rule*>::iterator i = rules.begin();
i != rules.end(); i++)
if ((*i)->getAsText() == s) {
delete rule;
rule = NULL;
break;
}
if (rule) {
//printf("adding rule %s\n", rule->getAsText().c_str());
rules.push_back(rule);
rulesDone = canSolve(puzzle, rules);
}
}
} while (! rulesDone);
}
/*static void printPuzzle(SolvedPuzzle &puzzle)
{
for (int i = 0; i < PUZZLE_SIZE; i++) {
char prefix = 'A' + i;
for (int j = 0; j < PUZZLE_SIZE; j++) {
if (j)
std::cout << " ";
std::cout << prefix << puzzle[i][j];
}
std::cout << std::endl;
}
}
static void printRules(Rules &rules)
{
for (Rules::iterator i = rules.begin(); i != rules.end(); i++)
std::cout << (*i)->getAsText() << std::endl;;
}*/
void genPuzzle(SolvedPuzzle &puzzle, Rules &rules)
{
for (int i = 0; i < PUZZLE_SIZE; i++) {
for (int j = 0; j < PUZZLE_SIZE; j++)
puzzle[i][j] = j + 1;
shuffle(puzzle[i]);
}
genRules(puzzle, rules);
removeRules(puzzle, rules);
//printPuzzle(puzzle);
//printRules(rules);
}
void openInitial(Possibilities &possib, Rules &rules)
{
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule *r = *i;
if (r->applyOnStart())
r->apply(possib);
}
}
void getHintsQty(Rules &rules, int &vert, int &horiz)
{
vert = 0;
horiz = 0;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule::ShowOptions so = (*i)->getShowOpts();
switch (so) {
case Rule::SHOW_VERT: vert++; break;
case Rule::SHOW_HORIZ: horiz++; break;
default: ;
}
}
}
void savePuzzle(SolvedPuzzle &puzzle, std::ostream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
writeInt(stream, puzzle[row][col]);
}
void loadPuzzle(SolvedPuzzle &puzzle, std::istream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
puzzle[row][col] = readInt(stream);
}
Rule* getRule(Rules &rules, int no)
{
int j = 0;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
if (j == no)
return *i;
j++;
}
throw Exception(L"Rule is not found");
}
/*int main(int argc, char *argv[])
{
srand(time(NULL));
Rules rules;
Puzzle puzzle;
genPuzzle(puzzle, rules);
printPuzzle(puzzle);
printRules(rules);
return 0;
}*/