Puzzle
Puzzle game

Puzzle.cpp
#include "CtrlLib/CtrlLib.h"
using namespace Upp;
#define LAYOUTFILE <Puzzle/Puzzle.lay>
#include <CtrlCore/lay.h>
#define IMAGEFILE <Puzzle/Puzzle.iml>
#define IMAGECLASS PuzzleImg
#include <Draw/iml.h>
#define TFILE <Puzzle/Puzzle.t>
#include <Core/t.h>
class Puzzle : public TopWindow {
public:
virtual void Paint(Draw& w);
virtual void LeftDown(Point p, dword);
private:
Buffer<int> box;
Size size;
Point hole;
int moves;
int lang;
VectorMap<String, int> score;
int& Get(int x, int y) { return box[size.cx * y + x]; }
bool Move(int x, int y);
void ShowStatus();
void Init();
void About();
void Scores();
void Setup();
void New();
void GameMenu(Bar& bar);
void MainMenu(Bar& bar);
MenuBar menu;
StatusBar status;
typedef Puzzle CLASSNAME;
public:
void Xmlize(XmlIO xml);
void Generate();
Puzzle();
};
bool Puzzle::Move(int x, int y)
{
if(x >= 0 && x < size.cx && y >= 0 && y < size.cy &&
(y == hole.y && abs(x - hole.x) == 1 || x == hole.x && abs(y - hole.y) == 1)) {
Swap(Get(x, y), Get(hole.x, hole.y));
hole = Point(x, y);
Refresh();
return true;
}
return false;
}
void Puzzle::ShowStatus()
{
String txt = t_("Moves: ");
txt << moves;
status = txt;
}
void Puzzle::Generate()
{
int n = size.cx * size.cy;
box.Alloc(n);
n--;
for(int i = 0; i < n; i++)
box[i] = i + 1;
box[n] = 0;
hole = size - 1;
n = 20 * size.cx * size.cy;
while(n > 0)
n -= Move(hole.x + (rand() % 3) - 1, hole.y + (rand() % 3) - 1);
moves = 0;
ShowStatus();
SetRect(GetWorkArea().CenterRect(AddFrameSize(size * 32)));
}
void Puzzle::Paint(Draw& w)
{
w.DrawRect(GetSize(), WhiteGray);
for(int y = 0; y < size.cy; y++)
for(int x = 0; x < size.cx; x++) {
int b = Get(x, y);
Point p(x * 32, y * 32);
if(b) {
w.DrawImage(p.x, p.y, PuzzleImg::BoxImg());
String txt = AsString(b);
Size sz = GetTextSize(txt, Arial(20).Bold());
w.DrawText(p.x + (32 - sz.cx) / 2, p.y + (32 - sz.cy) / 2, txt,
Arial(20).Bold(), SLtBlue);
}
}
}
void Puzzle::LeftDown(Point p, dword)
{
if(Move(p.x / 32, p.y / 32)) {
moves++;
ShowStatus();
int n = size.cx * size.cy - 1;
for(int i = 0; i < n; i++)
if(box[i] != i + 1)
return;
String dim;
int& sm = score.GetAdd(String().Cat() << size.cx << "x" << size.cy);
if(moves < sm) {
sm = moves;
PromptOK(t_("You win with the new best score!"));
}
else
PromptOK(t_("You win!"));
Generate();
}
else
BeepExclamation();
}
void Puzzle::About()
{
PromptOK("[A5 Puzzle]&Using [*^https`:`/`/www`.ultimatepp`.org`/^ U`+`+] technology.");
}
void Puzzle::Scores()
{
WithScoreLayout<TopWindow> d;
CtrlLayoutOK(d, t_("Best scores"));
d.score.AddColumn(t_("Dimension"));
d.score.AddColumn(t_("Moves"));
d.score.ColumnWidths("71 48");
d.score.NoCursor().NoGrid();
Vector<int> o = GetSortOrder(score.GetKeys());
for(int i = 0; i < o.GetCount(); i++)
d.score.Add(score.GetKey(o[i]), score[o[i]]);
d.Run();
}
void Puzzle::Setup()
{
WithSetupLayout<TopWindow> d;
CtrlLayoutOKCancel(d, t_("Puzzle setup"));
CtrlRetriever r;
Size sz = size;
r
(d.cx, size.cx)
(d.cy, size.cy)
(d.lang, lang)
;
if(d.Run() == IDOK) {
r.Retrieve();
Init();
if(sz != size)
Generate();
}
}
void Puzzle::New()
{
if(PromptYesNo(t_("Start a new game?")))
Generate();
}
void Puzzle::GameMenu(Bar& bar)
{
bar.Add(t_("New game"), THISBACK(New));
bar.Add(t_("Setup.."), THISBACK(Setup));
bar.Add(t_("Best scores.."), THISBACK(Scores));
bar.Separator();
bar.Add(t_("Exit"), Breaker());
}
void Puzzle::MainMenu(Bar& bar)
{
bar.Add(t_("Game"), THISBACK(GameMenu));
bar.Add(t_("About"), THISBACK(About));
}
void Puzzle::Init()
{
SetLanguage(lang);
SetDefaultCharset(CHARSET_UTF8);
menu.Set(THISBACK(MainMenu));
ShowStatus();
}
void Puzzle::Xmlize(XmlIO xml)
{
XmlizeLangAttr(xml, lang);
xml("Scores", score)
("Dimension", size);
Init();
}
Puzzle::Puzzle()
{
size = Size(4, 4);
AddFrame(menu);
AddFrame(status);
lang = LNG_ENGLISH;
Init();
BackPaint();
}
GUI_APP_MAIN
{
Puzzle p;
LoadFromXMLFile(p);
p.Generate();
p.Run();
StoreAsXMLFile(p);
}
Puzzle.lay
LAYOUT(SetupLayout, 152, 160)
ITEM(LabelBox, dv___0, SetLabel(t_("Dimension:")).LeftPosZ(8, 136).TopPosZ(4, 60))
ITEM(Label, dv___1, SetLabel(t_("Height:")).SetAlign(ALIGN_CENTER).LeftPosZ(80, 48).TopPosZ(20, 13))
ITEM(EditIntSpin, cx, Min(3).Max(15).NotNull(true).SetFrame(ThinInsetFrame()).LeftPosZ(16, 56).TopPosZ(36, 19))
ITEM(Label, dv___3, SetLabel(t_("Width:")).SetAlign(ALIGN_CENTER).LeftPosZ(16, 48).TopPosZ(20, 13))
ITEM(EditIntSpin, cy, Min(3).Max(15).NotNull(true).SetFrame(ThinInsetFrame()).LeftPosZ(80, 56).TopPosZ(36, 19))
ITEM(LabelBox, dv___5, SetLabel(t_("Language:")).LeftPosZ(8, 136).TopPosZ(72, 52))
ITEM(LNGCtrl, lang, LeftPosZ(32, 88).TopPosZ(92, 20))
ITEM(Button, ok, SetLabel(t_("OK")).HSizePosZ(80, 8).TopPosZ(132, 20))
ITEM(Button, cancel, SetLabel(t_("Cancel")).HSizePosZ(8, 80).TopPosZ(132, 20))
END_LAYOUT
LAYOUT(ScoreLayout, 156, 260)
ITEM(ArrayCtrl, score, LeftPosZ(8, 140).TopPosZ(44, 180))
ITEM(Button, ok, SetLabel(t_("OK")).LeftPosZ(84, 64).TopPosZ(232, 20))
ITEM(Label, dv___2, SetLabel(t_("Best scores")).SetAlign(ALIGN_CENTER).SetFont(Arial(20)).SetInk(Blue).LeftPosZ(8, 140).TopPosZ(8, 32))
END_LAYOUT
|