einstein/rules.cpp

564 lines
15 KiB
C++
Raw Normal View History

2013-09-21 21:05:25 +00:00
#include "puzgen.h"
#include "utils.h"
#include "main.h"
#include "convert.h"
#include "unicode.h"
static std::wstring getThingName(int row, int thing)
{
std::wstring s;
s += (wchar_t)(L'A' + row);
s += toString(thing);
return s;
}
class NearRule: public Rule
{
private:
int thing1[2];
int thing2[2];
public:
NearRule(SolvedPuzzle puzzle);
NearRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
private:
bool applyToCol(Possibilities &pos, int col, int nearRow, int nearNum,
int thisRow, int thisNum);
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_HORIZ; };
virtual void save(std::ostream &stream);
};
NearRule::NearRule(SolvedPuzzle puzzle)
{
int col1 = rndGen.genInt(PUZZLE_SIZE);
thing1[0] = rndGen.genInt(PUZZLE_SIZE);
thing1[1] = puzzle[thing1[0]][col1];
int col2;
if (col1 == 0)
col2 = 1;
else
if (col1 == PUZZLE_SIZE-1)
col2 = PUZZLE_SIZE-2;
else
if (rndGen.genInt(2))
col2 = col1 + 1;
else
col2 = col1 - 1;
thing2[0] = rndGen.genInt(PUZZLE_SIZE);
thing2[1] = puzzle[thing2[0]][col2];
}
NearRule::NearRule(std::istream &stream)
{
thing1[0] = readInt(stream);
thing1[1] = readInt(stream);
thing2[0] = readInt(stream);
thing2[1] = readInt(stream);
}
bool NearRule::applyToCol(Possibilities &pos, int col, int nearRow, int nearNum,
int thisRow, int thisNum)
{
bool hasLeft, hasRight;
if (col == 0)
hasLeft = false;
else
hasLeft = pos.isPossible(col - 1, nearRow, nearNum);
if (col == PUZZLE_SIZE-1)
hasRight = false;
else
hasRight = pos.isPossible(col + 1, nearRow, nearNum);
if ((! hasRight) && (! hasLeft) && pos.isPossible(col, thisRow, thisNum)) {
pos.exclude(col, thisRow, thisNum);
return true;
} else
return false;
}
bool NearRule::apply(Possibilities &pos)
{
bool changed = false;
for (int i = 0; i < PUZZLE_SIZE; i++) {
if (applyToCol(pos, i, thing1[0], thing1[1], thing2[0], thing2[1]))
changed = true;
if (applyToCol(pos, i, thing2[0], thing2[1], thing1[0], thing1[1]))
changed = true;
}
if (changed)
apply(pos);
return changed;
}
std::wstring NearRule::getAsText()
{
return getThingName(thing1[0], thing1[1]) +
L" is near to " + getThingName(thing2[0], thing2[1]);
}
void NearRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(thing1[0], thing1[1], h);
screen.draw(x, y, icon);
screen.draw(x + icon->h, y, iconSet.getNearHintIcon(h));
screen.draw(x + icon->h*2, y, iconSet.getLargeIcon(thing2[0], thing2[1], h));
}
void NearRule::save(std::ostream &stream)
{
writeString(stream, L"near");
writeInt(stream, thing1[0]);
writeInt(stream, thing1[1]);
writeInt(stream, thing2[0]);
writeInt(stream, thing2[1]);
}
class DirectionRule: public Rule
{
private:
int row1, thing1;
int row2, thing2;
public:
DirectionRule(SolvedPuzzle puzzle);
DirectionRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
private:
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_HORIZ; };
virtual void save(std::ostream &stream);
};
DirectionRule::DirectionRule(SolvedPuzzle puzzle)
{
row1 = rndGen.genInt(PUZZLE_SIZE);
row2 = rndGen.genInt(PUZZLE_SIZE);
int col1 = rndGen.genInt(PUZZLE_SIZE - 1);
int col2 = rndGen.genInt(PUZZLE_SIZE - col1 - 1) + col1 + 1;
thing1 = puzzle[row1][col1];
thing2 = puzzle[row2][col2];
}
DirectionRule::DirectionRule(std::istream &stream)
{
row1 = readInt(stream);
thing1 = readInt(stream);
row2 = readInt(stream);
thing2 = readInt(stream);
}
bool DirectionRule::apply(Possibilities &pos)
{
bool changed = false;
for (int i = 0; i < PUZZLE_SIZE; i++) {
if (pos.isPossible(i, row2, thing2)) {
pos.exclude(i, row2, thing2);
changed = true;
}
if (pos.isPossible(i, row1, thing1))
break;
}
for (int i = PUZZLE_SIZE-1; i >= 0; i--) {
if (pos.isPossible(i, row1, thing1)) {
pos.exclude(i, row1, thing1);
changed = true;
}
if (pos.isPossible(i, row2, thing2))
break;
}
return changed;
}
std::wstring DirectionRule::getAsText()
{
return getThingName(row1, thing1) +
L" is from the left of " + getThingName(row2, thing2);
}
void DirectionRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(row1, thing1, h);
screen.draw(x, y, icon);
screen.draw(x + icon->h, y, iconSet.getSideHintIcon(h));
screen.draw(x + icon->h*2, y, iconSet.getLargeIcon(row2, thing2, h));
}
void DirectionRule::save(std::ostream &stream)
{
writeString(stream, L"direction");
writeInt(stream, row1);
writeInt(stream, thing1);
writeInt(stream, row2);
writeInt(stream, thing2);
}
class OpenRule: public Rule
{
private:
int col, row, thing;
public:
OpenRule(SolvedPuzzle puzzle);
OpenRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
virtual bool applyOnStart() { return true; };
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted) { };
virtual ShowOptions getShowOpts() { return SHOW_NOTHING; };
virtual void save(std::ostream &stream);
};
OpenRule::OpenRule(SolvedPuzzle puzzle)
{
col = rndGen.genInt(PUZZLE_SIZE);
row = rndGen.genInt(PUZZLE_SIZE);
thing = puzzle[row][col];
}
OpenRule::OpenRule(std::istream &stream)
{
col = readInt(stream);
row = readInt(stream);
thing = readInt(stream);
}
bool OpenRule::apply(Possibilities &pos)
{
if (! pos.isDefined(col, row)) {
pos.set(col, row, thing);
return true;
} else
return false;
}
std::wstring OpenRule::getAsText()
{
return getThingName(row, thing) + L" is at column " + toString(col+1);
}
void OpenRule::save(std::ostream &stream)
{
writeString(stream, L"open");
writeInt(stream, col);
writeInt(stream, row);
writeInt(stream, thing);
}
class UnderRule: public Rule
{
private:
int row1, thing1, row2, thing2;
public:
UnderRule(SolvedPuzzle puzzle);
UnderRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_VERT; };
virtual void save(std::ostream &stream);
};
UnderRule::UnderRule(SolvedPuzzle puzzle)
{
int col = rndGen.genInt(PUZZLE_SIZE);
row1 = rndGen.genInt(PUZZLE_SIZE);
thing1 = puzzle[row1][col];
do {
row2 = rndGen.genInt(PUZZLE_SIZE);
} while (row2 == row1) ;
thing2 = puzzle[row2][col];
}
UnderRule::UnderRule(std::istream &stream)
{
row1 = readInt(stream);
thing1 = readInt(stream);
row2 = readInt(stream);
thing2 = readInt(stream);
}
bool UnderRule::apply(Possibilities &pos)
{
bool changed = false;
for (int i = 0; i < PUZZLE_SIZE; i++) {
if ((! pos.isPossible(i, row1, thing1)) &&
pos.isPossible(i, row2, thing2))
{
pos.exclude(i, row2, thing2);
changed = true;
}
if ((! pos.isPossible(i, row2, thing2)) &&
pos.isPossible(i, row1, thing1))
{
pos.exclude(i, row1, thing1);
changed = true;
}
}
return changed;
}
std::wstring UnderRule::getAsText()
{
return getThingName(row1, thing1) + L" is the same column as " +
getThingName(row2, thing2);
}
void UnderRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(row1, thing1, h);
screen.draw(x, y, icon);
screen.draw(x, y + icon->h, iconSet.getLargeIcon(row2, thing2, h));
}
void UnderRule::save(std::ostream &stream)
{
writeString(stream, L"under");
writeInt(stream, row1);
writeInt(stream, thing1);
writeInt(stream, row2);
writeInt(stream, thing2);
}
class BetweenRule: public Rule
{
private:
int row1, thing1;
int row2, thing2;
int centerRow, centerThing;
public:
BetweenRule(SolvedPuzzle puzzle);
BetweenRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
private:
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_HORIZ; };
virtual void save(std::ostream &stream);
};
BetweenRule::BetweenRule(SolvedPuzzle puzzle)
{
centerRow = rndGen.genInt(PUZZLE_SIZE);
row1 = rndGen.genInt(PUZZLE_SIZE);
row2 = rndGen.genInt(PUZZLE_SIZE);
int centerCol = rndGen.genInt(PUZZLE_SIZE - 2) + 1;
centerThing = puzzle[centerRow][centerCol];
if (rndGen.genInt(2)) {
thing1 = puzzle[row1][centerCol - 1];
thing2 = puzzle[row2][centerCol + 1];
} else {
thing1 = puzzle[row1][centerCol + 1];
thing2 = puzzle[row2][centerCol - 1];
}
}
BetweenRule::BetweenRule(std::istream &stream)
{
row1 = readInt(stream);
thing1 = readInt(stream);
row2 = readInt(stream);
thing2 = readInt(stream);
centerRow = readInt(stream);
centerThing = readInt(stream);
}
bool BetweenRule::apply(Possibilities &pos)
{
bool changed = false;
if (pos.isPossible(0, centerRow, centerThing)) {
changed = true;
pos.exclude(0, centerRow, centerThing);
}
if (pos.isPossible(PUZZLE_SIZE-1, centerRow, centerThing)) {
changed = true;
pos.exclude(PUZZLE_SIZE-1, centerRow, centerThing);
}
bool goodLoop;
do {
goodLoop = false;
for (int i = 1; i < PUZZLE_SIZE-1; i++) {
if (pos.isPossible(i, centerRow, centerThing)) {
if (! ((pos.isPossible(i-1, row1, thing1) &&
pos.isPossible(i+1, row2, thing2)) ||
(pos.isPossible(i-1, row2, thing2) &&
pos.isPossible(i+1, row1, thing1))))
{
pos.exclude(i, centerRow, centerThing);
goodLoop = true;
}
}
}
for (int i = 0; i < PUZZLE_SIZE; i++) {
bool leftPossible, rightPossible;
if (pos.isPossible(i, row2, thing2)) {
if (i < 2)
leftPossible = false;
else
leftPossible = (pos.isPossible(i-1, centerRow, centerThing)
&& pos.isPossible(i-2, row1, thing1));
if (i >= PUZZLE_SIZE - 2)
rightPossible = false;
else
rightPossible = (pos.isPossible(i+1, centerRow, centerThing)
&& pos.isPossible(i+2, row1, thing1));
if ((! leftPossible) && (! rightPossible)) {
pos.exclude(i, row2, thing2);
goodLoop = true;
}
}
if (pos.isPossible(i, row1, thing1)) {
if (i < 2)
leftPossible = false;
else
leftPossible = (pos.isPossible(i-1, centerRow, centerThing)
&& pos.isPossible(i-2, row2, thing2));
if (i >= PUZZLE_SIZE - 2)
rightPossible = false;
else
rightPossible = (pos.isPossible(i+1, centerRow, centerThing)
&& pos.isPossible(i+2, row2, thing2));
if ((! leftPossible) && (! rightPossible)) {
pos.exclude(i, row1, thing1);
goodLoop = true;
}
}
}
if (goodLoop)
changed = true;
} while (goodLoop);
return changed;
}
std::wstring BetweenRule::getAsText()
{
return getThingName(centerRow, centerThing) +
L" is between " + getThingName(row1, thing1) + L" and " +
getThingName(row2, thing2);
}
void BetweenRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(row1, thing1, h);
screen.draw(x, y, icon);
screen.draw(x + icon->w, y, iconSet.getLargeIcon(centerRow, centerThing, h));
screen.draw(x + icon->w*2, y, iconSet.getLargeIcon(row2, thing2, h));
SDL_Surface *arrow = iconSet.getBetweenArrow(h);
screen.draw(x + icon->w - (arrow->w - icon->w) / 2, y + 0, arrow);
}
void BetweenRule::save(std::ostream &stream)
{
writeString(stream, L"between");
writeInt(stream, row1);
writeInt(stream, thing1);
writeInt(stream, row2);
writeInt(stream, thing2);
writeInt(stream, centerRow);
writeInt(stream, centerThing);
}
Rule* genRule(SolvedPuzzle &puzzle)
{
int a = rndGen.genInt(14);
switch (a) {
case 0:
case 1:
case 2:
case 3: return new NearRule(puzzle);
case 4: return new OpenRule(puzzle);
case 5:
case 6: return new UnderRule(puzzle);
case 7:
case 8:
case 9:
case 10: return new DirectionRule(puzzle);
case 11:
case 12:
case 13: return new BetweenRule(puzzle);
default: return genRule(puzzle);
}
}
void saveRules(Rules &rules, std::ostream &stream)
{
writeInt(stream, rules.size());
for (Rules::iterator i = rules.begin(); i != rules.end(); i++)
(*i)->save(stream);
}
void loadRules(Rules &rules, std::istream &stream)
{
int no = readInt(stream);
for (int i = 0; i < no; i++) {
std::wstring ruleType = readString(stream);
Rule *r;
if (ruleType == L"near")
r = new NearRule(stream);
else if (ruleType == L"open")
r = new OpenRule(stream);
else if (ruleType == L"under")
r = new UnderRule(stream);
else if (ruleType == L"direction")
r = new DirectionRule(stream);
else if (ruleType == L"between")
r = new BetweenRule(stream);
else
throw Exception(L"invalid rule type " + ruleType);
rules.push_back(r);
}
}