![]() |
PortableGUI |
Hosted by SourceForge Project page |
/***
* Portable GUI
* Code for Huarong Path
* Copyright (C) 2001-2001 LittlePanda <littlepanda@mostlysunny.com>
*/
#include <portablegui.h>
#include "huarong.h"
class HRBoard;
class HRPiece;
class HRPossibleMove;
#define mokka &g_hMokka
#define choco &g_hChoco
PGColor g_hMokka(187,170,142);
PGColor g_hChoco(59,42,14);
PGFont* g_hBoldfont;
PGFrame* g_hMainFrame;
PGPanel* g_hStatus;
PGIcon* g_hIcon;
PGImage* g_hLogo;
HRBoard* g_hBoard;
PGPanel* g_hControl;
PGLabel* g_hSteps;
PGButton* g_hButtonNew;
PGButton* g_hButtonRandom;
PGButton* g_hButtonUndo;
PGButton* g_hButtonSolve;
PGButton* g_hButtonHelp;
PGButton* g_hButtonAbout;
PGButton* g_hButtonQuit;
void setSteps(int nb);
void enableUndoButton();
void disableUndoButton();
class HRBoard : public PGCanvas {
private:
HRPiece* piece[10];
float xUnit, yUnit;
HRPiece* pieceSelected;
int oldX, oldY, steps;
PGStack stack;
int possibles[10][4][2];
boolean refresh;
int margin;
PGString message;
PGDimension bufferdim;
PGImage bufferimg;
PGGraphics* buffergfx;
private:
HRPiece* selectPiece(int x, int y);
int gridifyX(int x);
int gridifyY(int y);
public:
HRBoard();
float getXUnit();
float getYUnit();
int getMargin();
void reposition();
void resetboard();
virtual void getPreferredSize(PGDimension* dim, BOOL recalc);
virtual void paint(PGGraphics* g);
virtual void update(PGGraphics* g);
int isEmpty(int x, int y);
void mousePressed(int x, int y);
void mouseReleased(int x, int y);
void randomize();
void autosolve();
void undo();
void possibleMoves(HRPossibleMove* marray);
int nbMovables(HRPossibleMove* marray, int idx);
int isDone();
};
class HRMouseListener : public PGMouseListener {
public:
virtual void mousePressed(PGMouseEvent* evt) {
g_hBoard->mousePressed(evt->getX(), evt->getY());
}
virtual void mouseReleased(PGMouseEvent* evt) {
g_hBoard->mouseReleased(evt->getX(), evt->getY());
}
};
class HRPiece {
private:
HRBoard *board;
int pwidth, pheight, xpos, ypos;
public:
HRPiece::HRPiece(HRBoard* b, int w, int h) {
board = b;
pwidth = w;
pheight = h;
}
void setPos(int x, int y) {
xpos = x;
ypos = y;
}
int getXPos() {
return xpos;
}
int getYPos() {
return ypos;
}
void show(PGGraphics* g) {
g->setColor(mokka);
int x1 = PGMathRound(xpos * board->getXUnit()) + board->getMargin();
int y1 = PGMathRound(ypos * board->getYUnit()) + board->getMargin();
int x2 = PGMathRound(pwidth * board->getXUnit()) - 2;
int y2 = PGMathRound(pheight * board->getYUnit()) - 2;
g->fillRect(x1,y1,x2,y2 );
g->draw3DRect(x1,y1,x2-1,y2-1,true);
}
void moveBy(PGGraphics* g, int x, int y) {
x += xpos;
y += ypos;
int x1 = PGMathRound(xpos * board->getXUnit()) + board->getMargin();
int y1 = PGMathRound(ypos * board->getYUnit()) + board->getMargin();
int x2 = PGMathRound(pwidth * board->getXUnit()) - 2;
int y2 = PGMathRound(pheight * board->getYUnit()) - 2;
if (g != NULL) {
g->setColor(PGCOLOR_BLACK);
g->fillRect(x1,y1,x2,y2);
}
setPos(x,y);
if (g != NULL)
show(g);
}
void moveBy(int x, int y) {
x += xpos;
y += ypos;
setPos(x,y);
}
boolean isSelected(int x, int y) {
return (xpos <= x && ypos <= y && x < (xpos + pwidth) && y < (ypos + pheight));
}
boolean isLegalMove(int* dir) {
if (PGMathAbs(dir[0]) > 2)
dir[0] = 2*dir[0]/PGMathAbs(dir[0]);
if (PGMathAbs(dir[1]) > 2)
dir[1] = 2*dir[1]/PGMathAbs(dir[1]);
int dirX = dir[0];
int dirY = dir[1];
if (dirX == 0 && dirY == 0)
return false;
if (PGMathAbs(pwidth*dirY)+PGMathAbs(pheight*dirX) > 2)
return false;
if (PGMathAbs(dirX) == 2) {
dir[0] /= 2;
if (isLegalMove(dir)) {
xpos += dir[0];
if (isLegalMove(dir))
dir[0] *= 2;
xpos -= dirX/2;
return true;
} else
return false;
}
if (PGMathAbs(dirY) == 2) {
dir[1] /= 2;
if (isLegalMove(dir)) {
ypos += dir[1];
if (isLegalMove(dir))
dir[1] *= 2;
ypos -= dirY/2;
return true;
} else
return false;
}
if (PGMathAbs(dirX) == 1 && PGMathAbs(dirY) == 1) {
dir[1] = 0;
if (isLegalMove(dir)) {
xpos += dirX;
dir[0] = 0;
dir[1] = dirY;
if (!isLegalMove(dir))
dir[1] = 0;
dir[0] = dirX;
xpos -= dirX;
return true;
} else {
dir[1] = dirY;
dir[0] = 0;
if (isLegalMove(dir)) {
ypos += dirY;
dir[0] = dirX;
dir[1] = 0;
if (!isLegalMove(dir))
dir[0] = 0;
dir[1] = dirY;
ypos -= dirY;
return true;
} else
return false;
}
}
int newX = xpos + dirX;
int newY = ypos + dirY;
if (newX < 0 || (newX + pwidth - 1) >= 4 || newY < 0 || (newY + pheight - 1) >= 5)
return false;
if (dirX == 1)
return (g_hBoard->isEmpty(xpos + pwidth, ypos) && g_hBoard->isEmpty(xpos + pwidth, ypos+pheight-1));
if (dirX == -1)
return (g_hBoard->isEmpty(xpos - 1, ypos)&& g_hBoard->isEmpty(xpos - 1, ypos + pheight - 1));
if (dirY == 1)
return (g_hBoard->isEmpty(xpos, ypos + pheight) && g_hBoard->isEmpty(xpos + pwidth - 1, ypos + pheight));
if (dirY == -1)
return (g_hBoard->isEmpty(xpos, ypos - 1) && g_hBoard->isEmpty(xpos+pwidth - 1, ypos - 1));
return false;
}
};
class HRMove : public PGObject {
public:
int px, py, dx, dy;
public:
HRMove(int* dir, HRPiece* piece) {
px = dir[0];
py = dir[1];
dx = piece->getXPos();
dy = piece->getYPos();
}
};
class HRPossibleMove : public PGObject {
public:
int dx, dy;
boolean possible;
public:
HRPossibleMove() {
dx = 0;
dy = 0;
possible = false;
}
void setPossible(boolean pos) {
possible = pos;
}
void setDirection(int x, int y) {
dx = x;
dy = y;
}
boolean isPossible() {
return possible;
}
};
HRPiece* HRBoard::selectPiece(int x, int y) {
for (int i = 0; i < 10; i++) {
if (piece[i]->isSelected(x, y)) {
return piece[i];
}
}
return NULL;
}
int HRBoard::gridifyX(int x) {
return (int)((x - margin)/xUnit);
}
int HRBoard::gridifyY(int y){
return (int)((y - margin)/yUnit);
}
HRBoard::HRBoard(){
piece[0] = new HRPiece(this, 2, 2);
piece[1] = new HRPiece(this, 1, 2);
piece[2] = new HRPiece(this, 1, 2);
piece[3] = new HRPiece(this, 1, 2);
piece[4] = new HRPiece(this, 1, 2);
piece[5] = new HRPiece(this, 2, 1);
piece[6] = new HRPiece(this, 1, 1);
piece[7] = new HRPiece(this, 1, 1);
piece[8] = new HRPiece(this, 1, 1);
piece[9] = new HRPiece(this, 1, 1);
int dirL[2];
dirL[0] = -1;
dirL[1] = 0;
int dirR[2];
dirR[0] = 1;
dirR[1] = 0;
int dirD[2];
dirD[0] = 0;
dirD[1] = -1;
int dirU[2];
dirU[0] = 0;
dirU[1] = 1;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 2; j++) {
possibles[i][0][j] = dirL[j];
possibles[i][1][j] = dirR[j];
possibles[i][2][j] = dirD[j];
possibles[i][3][j] = dirU[j];
}
}
margin = 8;
message = "";
resetboard();
reposition();
addMouseListener(new HRMouseListener());
}
float HRBoard::getXUnit(){
return xUnit;
}
float HRBoard::getYUnit(){
return yUnit;
}
int HRBoard::getMargin(){
return margin;
}
void HRBoard::reposition(){
piece[0]->setPos(1, 0);
piece[1]->setPos(0, 0);
piece[2]->setPos(0, 2);
piece[3]->setPos(3, 0);
piece[4]->setPos(3, 2);
piece[5]->setPos(1, 2);
piece[6]->setPos(0, 4);
piece[7]->setPos(1, 3);
piece[8]->setPos(2, 3);
piece[9]->setPos(3, 4);
repaint();
refresh = true;
}
void HRBoard::resetboard(){
message = "";
steps = 0;
stack.removeAll();
}
void HRBoard::getPreferredSize(PGDimension* dim, BOOL recalc) {
dim->width = 200;
dim->height = 200;
}
void HRBoard::paint(PGGraphics* g){
PGDimension d;
getSize(&d);
if ((buffergfx == NULL)
|| (d.width != bufferdim.width)
|| (d.height != bufferdim.height)) {
bufferdim = d;
xUnit = (d.width - 2*margin)/4.0f;
yUnit = (d.height - 2*margin)/5.0f;
createImage(d.width, d.height, &bufferimg);
refresh = true;
}
if (refresh) {
buffergfx = bufferimg.getGraphics();
buffergfx->setColor(PGCOLOR_BLACK);
buffergfx->fillRect(0, 0, d.width, d.height);
buffergfx->setColor(PGCOLOR_WHITE);
buffergfx->draw3DRect(2, 2, d.width-5, d.height-5, false);
buffergfx->draw3DRect(margin-2, margin-2, d.width-2*margin+3, d.height-2*margin+3, false);
buffergfx->setColor(PGCOLOR_BLACK);
buffergfx->fillRect(margin + PGMathRound(xUnit)-1, d.height-margin, PGMathRound(xUnit*2), margin);
for (int i = 0; i < 10; i++)
piece[i]->show(buffergfx);
refresh = false;
}
if (strcmp(message,"") != 0) {
buffergfx->setColor(choco);
buffergfx->setFont(this, g_hBoldfont);
buffergfx->drawString(message,12,d.height/2-12);
}
g->drawImage(&bufferimg, 0, 0);
}
void HRBoard::update(PGGraphics* g){
paint(g);
}
int HRBoard::isEmpty(int x, int y){
return (selectPiece(x, y) == NULL);
}
void HRBoard::mousePressed(int x, int y){
oldX = gridifyX(x);
oldY = gridifyY(y);
pieceSelected = selectPiece(oldX, oldY);
}
void HRBoard::mouseReleased(int x, int y){
if (pieceSelected != NULL) {
x = gridifyX(x);
y = gridifyY(y);
int dir[2];
dir[0] = x - oldX;
dir[1] = y - oldY;
if (pieceSelected->isLegalMove(dir)) {
oldX = pieceSelected->getXPos();
oldY = pieceSelected->getYPos();
if (steps == 0)
enableUndoButton();
pieceSelected->moveBy(buffergfx, dir[0], dir[1]);
HRMove* move = new HRMove(dir, pieceSelected);
stack.push(move);
PGINCREF(move);
steps++;
setSteps(steps);
if (isDone()) {
message = "Puzzle solved! :O)";
} else {
if (strcmp(message,"") != 0){
message = "";
refresh = true;
}
}
repaint();
}
pieceSelected = NULL;
}
}
void HRBoard::randomize(){
PGRandom* randomizer = new PGRandom();
PGINCREF(randomizer);
HRPossibleMove movables[40];
for (int i = 0; i < 10000; i++) {
possibleMoves(movables);
int s = randomizer->nextInt(10);
int nb = nbMovables(movables,s);
while (nb == 0) {
s = randomizer->nextInt(10);
nb = nbMovables(movables,s);
}
int posidx = 0;
int idx = randomizer->nextInt(nb);
for (int ctr = 0; ctr < 4; ctr++) {
if (movables[s*4+ctr].isPossible()) {
if (posidx == idx) {
piece[s]->moveBy(movables[s*4+ctr].dx, movables[s*4+ctr].dy);
break;
}
posidx++;
}
}
}
PGDECREF(randomizer);
refresh = true;
repaint();
}
int HRBoard::nbMovables(HRPossibleMove* marray, int idx) {
int nb = 0;
for(int i = 0; i < 4; i++) {
if (marray[idx*4+i].isPossible()) nb++;
}
return nb;
}
void HRBoard::autosolve(){
piece[0]->setPos(2, 3);
piece[1]->setPos(0, 0);
piece[2]->setPos(1, 0);
piece[3]->setPos(2, 0);
piece[4]->setPos(3, 0);
piece[5]->setPos(0, 2);
piece[6]->setPos(2, 2);
piece[7]->setPos(3, 2);
piece[8]->setPos(0, 3);
piece[9]->setPos(0, 4);
refresh = true;
repaint();
}
void HRBoard::undo(){
if (! stack.empty()) {
HRMove* move = (HRMove*)stack.pop();
int newX = move->dx;
int newY = move->dy;
int dirX = move->px;
int dirY = move->py;
PGDECREF(move);
selectPiece(newX, newY)->moveBy(buffergfx, -dirX, -dirY);
if (isDone()) {
message = "Puzzle solved! :O)";
} else {
if (strcmp(message, "") != 0){
message = "";
refresh = true;
}
}
repaint();
steps --;
if (steps == 0)
disableUndoButton();
setSteps(steps);
} else {
steps = 0;
disableUndoButton();
}
}
void HRBoard::possibleMoves(HRPossibleMove *movables){
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 4; j++) {
int idir[2];
idir[0] = possibles[i][j][0];
idir[1] = possibles[i][j][1];
if (piece[i]->isLegalMove(idir)) {
movables[i*4+j].setPossible(true);
movables[i*4+j].setDirection(idir[0], idir[1]);
} else
movables[i*4+j].setPossible(false);
}
}
}
int HRBoard::isDone(){
return (piece[0]->getXPos() == 1 && piece[0]->getYPos() == 3);
}
class MyWindowListener : public PGWindowListener {
virtual void windowClosing(PGWindow* wnd) {
getPGApplication()->exit(0);
}
};
void setSteps(int nb) {
if (nb == 1) g_hSteps->setText(STR(nb) + STR(" step"));
else g_hSteps->setText(STR(nb) + STR(" steps"));
}
void enableAllButtons() {
g_hButtonNew->setEnabled(true);
g_hButtonRandom->setEnabled(true);
g_hButtonSolve->setEnabled(true);
g_hButtonHelp->setEnabled(true);
g_hButtonAbout->setEnabled(true);
g_hButtonQuit->setEnabled(true);
}
void disableAllButtons() {
g_hButtonNew->setEnabled(false);
g_hButtonRandom->setEnabled(false);
g_hButtonSolve->setEnabled(false);
g_hButtonHelp->setEnabled(false);
g_hButtonAbout->setEnabled(false);
g_hButtonQuit->setEnabled(false);
}
void enableUndoButton() {
g_hButtonUndo->setEnabled(true);
}
void disableUndoButton() {
g_hButtonUndo->setEnabled(false);
}
class HRButtonNewListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
g_hBoard->resetboard();
setSteps(0);
disableUndoButton();
g_hBoard->reposition();
}
};
class HRButtonRandomListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
g_hBoard->resetboard();
setSteps(0);
disableUndoButton();
disableAllButtons();
g_hBoard->randomize();
enableAllButtons();
}
};
class HRButtonUndoListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
g_hBoard->undo();
}
};
class HRButtonSolveListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
g_hBoard->resetboard();
setSteps(0);
disableUndoButton();
g_hBoard->autosolve();
}
};
class HRButtonHelpListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
PGStyle* style = new PGStyle();
style->setFont(new PGFont("Dialog", PGFONT_BOLD | PGFONT_ITALIC, 16));
// Create help dialog
PGDialog help(g_hMainFrame, "Help - Huarong Path Puzzle");
help.addWindowListener(new PGCloseWindowListener());
help.setLayout(new PGBorderLayout(1,1,5,5));
PGTextArea* ta = new PGTextArea("The goal of this puzzle is to move the big square to the exit at the bottom center of the game board.", 6, 30, 0);
ta->append("\r\n");
ta->append("Use the mouse to drag the pieces.");
ta->setEditable(false);
help.add(ta, PGBORDERLAYOUT_CENTER);
// Create and add close button
PGButton* ok = new PGButton(" &Close ", true);
ok->setToolTipText("Close this window");
ok->addActionListener(new PGDialogCancelListener(&help));
help.add(ok, PGBORDERLAYOUT_SOUTH);
// Pack components in window and show modal
help.pack();
help.doModal();
}
};
class HRButtonAboutListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
PGStyle* style = new PGStyle();
style->setFont(new PGFont("Dialog", PGFONT_BOLD | PGFONT_ITALIC, 16));
// Create about dialog
PGDialog about(g_hMainFrame, "About Huarong Path Puzzle");
about.addWindowListener(new PGCloseWindowListener());
about.setLayout(new PGBorderLayout(1,1,5,5));
PGTextArea* ta = new PGTextArea("Hua Rong Path Puzzle v1.0", 6, 30, 0);
ta->append("\r\n");
ta->append("Author: LittlePanda <littlepanda@mostlysunny.com>");
ta->append("Version: v 1.0");
ta->append("Last Update: 18 June 2001");
ta->setEditable(false);
about.add(ta, PGBORDERLAYOUT_CENTER);
// Create and add close button
PGButton* ok = new PGButton(" &Close ", true);
ok->setToolTipText("Close this window");
ok->addActionListener(new PGDialogCancelListener(&about));
about.add(ok, PGBORDERLAYOUT_SOUTH);
// Pack components in window and show modal
about.pack();
about.doModal();
}
};
class HRButtonQuitListener : public PGActionListener {
public:
virtual void actionPerformed(PGActionListenerParent* from) {
getPGApplication()->exit(0);
}
};
PGPanel* makeControlPanel(PGFrame* frame) {
PGPanel* panel = new PGPanel();
panel->setLayout(new PGPercentLayout("12% 12% 12% 12% 12% 12% 12% 4%d 12%", 4, 0, true));
g_hButtonNew = new PGButton("New");
g_hButtonNew->addActionListener(new HRButtonNewListener());
g_hButtonRandom = new PGButton("Random");
g_hButtonRandom->addActionListener(new HRButtonRandomListener());
g_hButtonUndo = new PGButton("Undo");
g_hButtonUndo->addActionListener(new HRButtonUndoListener());
g_hButtonUndo->setEnabled(false);
g_hButtonSolve = new PGButton("Solve");
g_hButtonSolve->addActionListener(new HRButtonSolveListener());
//g_hButtonSolve->setEnabled(false);
g_hButtonHelp = new PGButton("Help");
g_hButtonHelp->addActionListener(new HRButtonHelpListener());
g_hButtonAbout = new PGButton("About");
g_hButtonAbout->addActionListener(new HRButtonAboutListener());
g_hButtonQuit = new PGButton("Quit");
g_hButtonQuit->addActionListener(new HRButtonQuitListener());
g_hSteps = new PGLabel("0 steps");
panel->add(g_hButtonNew);
panel->add(g_hButtonRandom);
panel->add(g_hButtonUndo);
panel->add(g_hButtonSolve);
panel->add(g_hButtonHelp);
panel->add(g_hButtonAbout);
panel->add(g_hButtonQuit);
panel->add(g_hSteps);
return panel;
}
PGPanel* makeStatusPanel(PGFrame* frame) {
// Create style for status line
PGStyle* style = new PGStyle();
style->setFont(new PGFont("Dialog", PGFONT_PLAIN, 8));
// Create panel
PGPanel* panel = new PGPanel();
panel->setLayout(new PGBorderLayout(3, 3));
// Create and add status line
PGLabel* status = new PGLabel("Huarong Path v 1.0", PGLABEL_SUNKEN);
status->setStyle(style);
panel->add(status, PGBORDERLAYOUT_CENTER);
// Add panel to main frame
frame->setStatusDisplay(status);
return panel;
}
PGImage* loadMemoryGIF(PGWindow* owner, const PGBYTE* data, long size) {
PGImage* image = new PGImage(owner);
PGMemoryInputStream file(data, size);
PGImageReader reader(image);
image->m_iError = PGLoadGIF(&file, &reader);
return image;
}
PGMAINPROC {
PGApplication app;
// Initialize the app using Mainproc args
PGINIT(app);
// Create main frame window
g_hMainFrame = new PGFrame("HuaRong Path");
PGINCREF(g_hMainFrame);
g_hBoldfont = new PGFont("Dialog", PGFONT_BOLD, 16);
PGINCREF(g_hBoldfont);
// Load Huarong title image
g_hLogo = loadMemoryGIF(g_hMainFrame, HUARONG_IMG, HUARONG_IMG_SIZE);
PGINCREF(g_hLogo);
// Add window listener to frame
g_hMainFrame->addWindowListener(new MyWindowListener());
// Use simple layoutmanager
g_hMainFrame->setLayout(new PGBorderLayout(5,5,8,8));
// Add the panels
g_hMainFrame->add(new PGImageViewer(g_hLogo), PGBORDERLAYOUT_NORTH);
g_hControl = makeControlPanel(g_hMainFrame);
g_hMainFrame->add(g_hControl, PGBORDERLAYOUT_EAST);
g_hStatus = makeStatusPanel(g_hMainFrame);
g_hMainFrame->add(g_hStatus, PGBORDERLAYOUT_SOUTH);
// Add the main Huarong Board
g_hBoard = new HRBoard();
g_hMainFrame->add(g_hBoard, PGBORDERLAYOUT_CENTER);
// Pack and show the frame
g_hMainFrame->pack();
g_hMainFrame->setVisible(true);
// Run the main event loop
PGEXITCODE exitCode = app.run();
// Free global references
PGDECREF(g_hBoldfont);
PGDECREF(g_hMainFrame);
PGDECREF(g_hLogo);
return exitCode;
}
| Syntax highlighted by Code2HTML, v. 0.9 |
Back to PortableGUI