mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
522 lines
14 KiB
C++
522 lines
14 KiB
C++
/*
|
|
YPuzzle copyright (c)2005 Arlen Albert Keshabian
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any
|
|
damages arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any
|
|
purpose, excluding commercial applications. To use, alter it and
|
|
redistribute it freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must
|
|
not claim that you wrote the original software. If you use this
|
|
software in a product, an acknowledgment in the product documentation
|
|
would be appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and
|
|
must not be misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
distribution.
|
|
*/
|
|
|
|
#include "YPuzzle.h"
|
|
|
|
#define TFILE <YPuzzle/YPuzzle.t>
|
|
#include <Core/t.h>
|
|
|
|
#define IMAGEFILE <YPuzzle/YPuzzle.iml>
|
|
#include <Draw/iml_source.h>
|
|
|
|
#include "TinyXML/tinyxml.h"
|
|
|
|
template<class T> void YSwap(T &A, T &B)
|
|
{
|
|
A ^= B ^= A ^= B;
|
|
}
|
|
|
|
CYPuzzleCell::CYPuzzleCell(int iID, YPuzzle *pParent)
|
|
{
|
|
m_iID = iID;
|
|
m_iCurID = m_iID;
|
|
|
|
m_pParent = pParent;
|
|
|
|
NoIgnoreMouse();
|
|
NoTransparent();
|
|
|
|
SetInk(Color(0, 0, 203));
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
SetFont(Tahoma(18).Bold());
|
|
#else
|
|
SetFont(Arial(18).Bold());
|
|
#endif
|
|
|
|
SetLabel(FormatInt(m_iID + 1));
|
|
SetAlign(ALIGN_CENTER);
|
|
SetVAlign(ALIGN_CENTER);
|
|
}
|
|
|
|
void CYPuzzleCell::LeftDown(Point p, dword keyflags)
|
|
{
|
|
m_pParent->Process(m_iID);
|
|
}
|
|
|
|
void CYPuzzleCell::Paint(Draw &draw)
|
|
{
|
|
Size l_Size = GetSize();
|
|
draw.DrawImage(0, 0, l_Size.cx, l_Size.cy, backimage);
|
|
PaintLabel(draw, 0, 0, l_Size.cx, l_Size.cy, !IsShowEnabled(), false, false, VisibleAccessKeys());
|
|
|
|
//UltimateCPP lacks 'DrawRoundedRect' function.
|
|
//So, the next lines of code roughly emulate it.
|
|
|
|
int l_iOffset = 4;
|
|
int l_iLineWidth = 2;
|
|
Color l_Color(0, 0, 203);
|
|
|
|
Rect l_Rect = Rect(GetSize());
|
|
|
|
draw.DrawLine(l_Rect.left + l_iOffset, l_Rect.top, l_Rect.right - l_iOffset, l_Rect.top, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.right - l_iOffset, l_Rect.top, l_Rect.right, l_Rect.top + l_iOffset, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.right, l_Rect.top + l_iOffset, l_Rect.right, l_Rect.bottom - l_iOffset, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.right, l_Rect.bottom - l_iOffset, l_Rect.right - l_iOffset, l_Rect.bottom, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.right - l_iOffset, l_Rect.bottom, l_Rect.left + l_iOffset, l_Rect.bottom, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.left + l_iOffset, l_Rect.bottom, l_Rect.left, l_Rect.bottom - l_iOffset, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.left, l_Rect.bottom - l_iOffset, l_Rect.left, l_Rect.top + l_iOffset, l_iLineWidth, l_Color);
|
|
draw.DrawLine(l_Rect.left, l_Rect.top + l_iOffset, l_Rect.left + l_iOffset, l_Rect.top, l_iLineWidth, l_Color);
|
|
}
|
|
|
|
YPuzzle::YPuzzle()
|
|
{
|
|
m_iLanguage = LNG_ENGLISH;
|
|
m_sConfigFilePath = "YPuzzle.config.xml";
|
|
|
|
int l_iX = 4, l_iY = 4, l_iCellSize = 64;
|
|
|
|
TiXmlDocument l_XMLDoc(m_sConfigFilePath);
|
|
if(l_XMLDoc.LoadFile())
|
|
{
|
|
TiXmlElement *l_pXMLRoot = l_XMLDoc.FirstChildElement("Configuration");
|
|
if(l_pXMLRoot)
|
|
{
|
|
TiXmlElement *l_pXMLLanguage = l_pXMLRoot->FirstChildElement("Language");
|
|
if(l_pXMLLanguage)
|
|
{
|
|
string l_sLanguage(l_pXMLLanguage->Attribute("id"));
|
|
if(l_sLanguage.length() == 5 && l_sLanguage[2] == '-')
|
|
m_iLanguage = LNG_(l_sLanguage[0], l_sLanguage[1], l_sLanguage[3], l_sLanguage[4]);
|
|
}
|
|
|
|
TiXmlElement *l_pXMLDimension = l_pXMLRoot->FirstChildElement("Dimension");
|
|
if(l_pXMLDimension)
|
|
{
|
|
l_pXMLDimension->Attribute("x", &l_iX);
|
|
l_pXMLDimension->Attribute("y", &l_iY);
|
|
l_pXMLDimension->Attribute("cell", &l_iCellSize);
|
|
|
|
if(l_iX < 3)
|
|
l_iX = 3;
|
|
if(l_iX > 15)
|
|
l_iX = 15;
|
|
|
|
if(l_iY < 3)
|
|
l_iY = 3;
|
|
if(l_iY > 15)
|
|
l_iY = 15;
|
|
|
|
if(l_iCellSize < 32)
|
|
l_iCellSize = 32;
|
|
if(l_iCellSize > 64)
|
|
l_iCellSize = 64;
|
|
}
|
|
|
|
TiXmlElement *l_pXMLScores = l_pXMLRoot->FirstChildElement("Scores");
|
|
if(l_pXMLScores)
|
|
{
|
|
TiXmlElement *l_XMLPlane = l_pXMLScores->FirstChildElement("Plane");
|
|
while(l_XMLPlane)
|
|
{
|
|
String l_sDimension = l_XMLPlane->Attribute("dimension");
|
|
map<String, int>::iterator l_It = m_msiScores.find(l_sDimension);
|
|
|
|
int l_iBestNumberOfMoves = -1;
|
|
l_XMLPlane->Attribute("best", &l_iBestNumberOfMoves);
|
|
|
|
if(l_iBestNumberOfMoves < -1)
|
|
l_iBestNumberOfMoves = -1;
|
|
|
|
if(l_It != m_msiScores.end())
|
|
(*l_It).second = l_iBestNumberOfMoves;
|
|
else
|
|
m_msiScores.insert(make_pair(l_sDimension, l_iBestNumberOfMoves));
|
|
|
|
l_XMLPlane = l_XMLPlane->NextSibling("Plane")->ToElement();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLanguage(m_iLanguage);
|
|
|
|
String l_sTitle("YPuzzle ");
|
|
l_sTitle += t_("game");
|
|
Title(l_sTitle);
|
|
Icon(smallicon);
|
|
MinimizeBox(true);
|
|
|
|
srand(GetTickCount());
|
|
|
|
m_iCellSize = 0;
|
|
m_iNumberOfCellsX = 0;
|
|
m_iNumberOfCellsY = 0;
|
|
m_iMaxNumberOfCells = m_iNumberOfCellsX * m_iNumberOfCellsY;
|
|
m_iCurEmptyID = m_iMaxNumberOfCells - 1;
|
|
|
|
String l_sScore = FormatInt(l_iX) + 'x' + FormatInt(l_iY);
|
|
|
|
map<String, int>::iterator l_It = m_msiScores.find(l_sScore);
|
|
if(l_It != m_msiScores.end())
|
|
m_iBestNumberOfMoves = (*l_It).second;
|
|
else
|
|
m_iBestNumberOfMoves = -1;
|
|
|
|
m_iNumberOfMoves = 0;
|
|
|
|
AddChild(&m_StatusBar);
|
|
m_StatusBar.NoTransparent();
|
|
|
|
AddChild(&m_Options);
|
|
m_Options.SetLabel("Y");
|
|
m_Options.NoWantFocus();
|
|
m_Options <<= THISBACK(OnOptions);
|
|
|
|
BuildMatrix(l_iX, l_iY, l_iCellSize);
|
|
}
|
|
|
|
bool YPuzzle::Key(dword key, int count)
|
|
{
|
|
bool l_bRes = TopWindow::Key(key, count);
|
|
|
|
if(key == K_SPACE)
|
|
{
|
|
ShuffleVector();
|
|
ArrangeButtons(m_viShuffle);
|
|
return true;
|
|
}
|
|
|
|
return l_bRes;
|
|
}
|
|
|
|
void YPuzzle::OnOptions()
|
|
{
|
|
WithYPuzzleDimensionLayout<TopWindow> l_OptionsDlg;
|
|
|
|
l_OptionsDlg.ok.Ok();
|
|
l_OptionsDlg.cancel.Cancel();
|
|
CtrlLayoutOKCancel(l_OptionsDlg, t_("Options"));
|
|
|
|
l_OptionsDlg.applang.SetData(m_iLanguage);
|
|
l_OptionsDlg.Xdim <<= m_iNumberOfCellsX;
|
|
l_OptionsDlg.Ydim <<= m_iNumberOfCellsY;
|
|
l_OptionsDlg.CellSize <<= m_iCellSize;
|
|
|
|
int l_iRet = l_OptionsDlg.Run();
|
|
if(l_iRet == IDCANCEL)
|
|
return;
|
|
|
|
int l_iNumberOfCellsX = l_OptionsDlg.Xdim.GetData();
|
|
int l_iNumberOfCellsY = l_OptionsDlg.Ydim.GetData();
|
|
int l_iLanguage = l_OptionsDlg.applang.GetData();
|
|
int l_iCellSize = l_OptionsDlg.CellSize.GetData();
|
|
|
|
String l_sScore = FormatInt(m_iNumberOfCellsX) + 'x' + FormatInt(m_iNumberOfCellsY);
|
|
map<String, int>::iterator l_It = m_msiScores.find(l_sScore);
|
|
if(l_It != m_msiScores.end())
|
|
(*l_It).second = m_iBestNumberOfMoves;
|
|
else
|
|
m_msiScores.insert(make_pair(l_sScore, m_iBestNumberOfMoves));
|
|
|
|
l_sScore = FormatInt(l_iNumberOfCellsX) + 'x' + FormatInt(l_iNumberOfCellsY);
|
|
l_It = m_msiScores.find(l_sScore);
|
|
if(l_It != m_msiScores.end())
|
|
m_iBestNumberOfMoves = (*l_It).second;
|
|
else
|
|
m_iBestNumberOfMoves = -1;
|
|
|
|
if(m_iLanguage != l_iLanguage)
|
|
{
|
|
|
|
m_iLanguage = l_iLanguage;
|
|
SetLanguage(l_iLanguage);
|
|
|
|
String l_sTitle("YPuzzle ");
|
|
l_sTitle += t_("game");
|
|
Title(l_sTitle);
|
|
|
|
if(l_iNumberOfCellsX == m_iNumberOfCellsX && l_iNumberOfCellsY == m_iNumberOfCellsY)
|
|
{
|
|
String l_sFormat = t_(" Moves: ");
|
|
l_sFormat += "%d";
|
|
String l_sStr(Format(l_sFormat, m_iNumberOfMoves));
|
|
if(m_iBestNumberOfMoves != -1)
|
|
{
|
|
String l_sStr2(Format(" (%d)", m_iBestNumberOfMoves));
|
|
l_sStr += l_sStr2;
|
|
}
|
|
|
|
m_StatusBar.SetLabel(l_sStr);
|
|
}
|
|
}
|
|
|
|
BuildMatrix(l_iNumberOfCellsX, l_iNumberOfCellsY, l_iCellSize);
|
|
}
|
|
|
|
void YPuzzle::Paint(Draw &draw)
|
|
{
|
|
Rect l_Rect = Rect(GetSize());
|
|
draw.DrawRect(l_Rect, Color(0, 0, 0));
|
|
}
|
|
|
|
YPuzzle::~YPuzzle()
|
|
{
|
|
vector<CYPuzzleCell*>::iterator Idx = m_Cells.begin();
|
|
for(; Idx != m_Cells.end(); Idx++)
|
|
delete *Idx;
|
|
|
|
String l_sScore = FormatInt(m_iNumberOfCellsX) + 'x' + FormatInt(m_iNumberOfCellsY);
|
|
map<String, int>::iterator l_It = m_msiScores.find(l_sScore);
|
|
if(l_It != m_msiScores.end())
|
|
(*l_It).second = m_iBestNumberOfMoves;
|
|
else
|
|
m_msiScores.insert(make_pair(l_sScore, m_iBestNumberOfMoves));
|
|
|
|
TiXmlDeclaration l_XMLDeclaration("1.0", "UTF-8", "yes");
|
|
TiXmlDocument l_XMLDoc(m_sConfigFilePath);
|
|
l_XMLDoc.InsertEndChild(l_XMLDeclaration);
|
|
|
|
TiXmlElement l_XMLConfig("Configuration");
|
|
TiXmlElement l_XMLLanguage("Language");
|
|
l_XMLLanguage.SetAttribute("id", LNGAsText(m_iLanguage));
|
|
TiXmlElement l_XMLDimension("Dimension");
|
|
l_XMLDimension.SetAttribute("x", m_iNumberOfCellsX);
|
|
l_XMLDimension.SetAttribute("y", m_iNumberOfCellsY);
|
|
l_XMLDimension.SetAttribute("cell", m_iCellSize);
|
|
TiXmlElement l_XMLScores("Scores");
|
|
|
|
TiXmlElement l_XMLScore("Plane");
|
|
l_It = m_msiScores.begin();
|
|
for(; l_It != m_msiScores.end(); l_It++)
|
|
{
|
|
l_XMLScore.SetAttribute("dimension", (*l_It).first);
|
|
l_XMLScore.SetAttribute("best", (*l_It).second);
|
|
l_XMLScores.InsertEndChild(l_XMLScore);
|
|
}
|
|
|
|
l_XMLConfig.InsertEndChild(l_XMLLanguage);
|
|
l_XMLConfig.InsertEndChild(l_XMLDimension);
|
|
l_XMLConfig.InsertEndChild(l_XMLScores);
|
|
l_XMLDoc.InsertEndChild(l_XMLConfig);
|
|
|
|
l_XMLDoc.SaveFile();
|
|
}
|
|
|
|
GUI_APP_MAIN
|
|
{
|
|
YPuzzle().Run();
|
|
}
|
|
|
|
void YPuzzle::ShuffleVector()
|
|
{
|
|
m_viShuffle.erase(m_viShuffle.begin(), m_viShuffle.end());
|
|
for(int iIndex = 0; iIndex < m_iMaxNumberOfCells - 1; iIndex++)
|
|
m_viShuffle.push_back(iIndex);
|
|
|
|
random_shuffle(m_viShuffle.begin(), m_viShuffle.end());
|
|
|
|
m_iCurEmptyID = m_iMaxNumberOfCells - 1;
|
|
|
|
CheckAndFixParity(m_viShuffle);
|
|
}
|
|
|
|
void YPuzzle::CheckAndFixParity(vector<int> &viShuffledVector)
|
|
{
|
|
int l_iParity = 0;
|
|
|
|
for(vector<int>::iterator Idx = viShuffledVector.begin(); Idx != viShuffledVector.end(); Idx++)
|
|
{
|
|
for(vector<int>::iterator Idx1 = Idx + 1; Idx1 != viShuffledVector.end(); Idx1++)
|
|
if((*Idx) > (*Idx1))
|
|
l_iParity++;
|
|
}
|
|
|
|
if(l_iParity & 1)
|
|
YSwap(viShuffledVector[0], viShuffledVector[1]);
|
|
}
|
|
|
|
void YPuzzle::BuildMatrix(int CX, int CY, int iCellSize)
|
|
{
|
|
if(CX == m_iNumberOfCellsX &&
|
|
CY == m_iNumberOfCellsY &&
|
|
iCellSize == m_iCellSize &&
|
|
m_Cells.size())
|
|
return;
|
|
|
|
m_iNumberOfCellsX = CX;
|
|
m_iNumberOfCellsY = CY;
|
|
m_iCellSize = iCellSize;
|
|
m_iMaxNumberOfCells = m_iNumberOfCellsX * m_iNumberOfCellsY;
|
|
m_iCurEmptyID = m_iMaxNumberOfCells - 1;
|
|
|
|
int l_iStatusBarHeight = 16;
|
|
Rect l_WindowRect(0, 0, m_iCellSize * m_iNumberOfCellsX, m_iCellSize * m_iNumberOfCellsY + l_iStatusBarHeight);
|
|
|
|
Hide();
|
|
|
|
Rect l_Desktop = Ctrl::GetWorkArea();
|
|
Rect l_CenteredRect = l_Desktop.CenterRect(min(l_Desktop.Size(), l_WindowRect.Size()));
|
|
SetRect(l_CenteredRect);
|
|
|
|
Rect l_ClientRect(l_CenteredRect);
|
|
l_ClientRect.Offset(-l_CenteredRect.left, -l_CenteredRect.top);
|
|
|
|
int l_iStatusTop = l_ClientRect.bottom - l_iStatusBarHeight;
|
|
m_StatusBar.SetRect(Rect(0, l_iStatusTop, l_ClientRect.right - l_iStatusBarHeight * 2, l_ClientRect.bottom));
|
|
m_Options.SetRect(Rect(l_ClientRect.right - l_iStatusBarHeight * 2, l_iStatusTop, l_ClientRect.right, l_ClientRect.bottom));
|
|
|
|
Show();
|
|
|
|
int Idx = 0;
|
|
|
|
Rect l_CellRect(0, 0, m_iCellSize, m_iCellSize);
|
|
CYPuzzleCell *l_pCell = NULL;
|
|
|
|
if((int)m_Cells.size() < m_iMaxNumberOfCells - 1)
|
|
{
|
|
for(int Idx = (int)m_Cells.size(); Idx < m_iMaxNumberOfCells - 1; Idx++)
|
|
{
|
|
l_pCell = new CYPuzzleCell(Idx, this);
|
|
|
|
AddChild(l_pCell);
|
|
|
|
m_Cells.push_back(l_pCell);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(vector<CYPuzzleCell*>::iterator Idx = m_Cells.begin() + m_iMaxNumberOfCells - 1; Idx != m_Cells.end(); Idx++)
|
|
delete *Idx;
|
|
|
|
m_Cells.erase(m_Cells.begin() + m_iMaxNumberOfCells - 1, m_Cells.end());
|
|
}
|
|
|
|
ShuffleVector();
|
|
ArrangeButtons(m_viShuffle);
|
|
}
|
|
|
|
void YPuzzle::Process(int iIndex)
|
|
{
|
|
int l_iCurDivY = m_Cells[iIndex]->m_iCurID / m_iNumberOfCellsX;
|
|
int l_iCurDivX = m_Cells[iIndex]->m_iCurID % m_iNumberOfCellsX;
|
|
|
|
int l_iEmptyDivY = m_iCurEmptyID / m_iNumberOfCellsX;
|
|
int l_iEmptyDivX = m_iCurEmptyID % m_iNumberOfCellsX;
|
|
|
|
int l_iDirection = 0;
|
|
|
|
if(l_iCurDivX == l_iEmptyDivX)
|
|
{
|
|
if(l_iCurDivY + 1 == l_iEmptyDivY)
|
|
l_iDirection = 3;
|
|
else
|
|
if(l_iCurDivY - 1 == l_iEmptyDivY)
|
|
l_iDirection = 1;
|
|
}
|
|
else
|
|
{
|
|
if(l_iCurDivY == l_iEmptyDivY)
|
|
{
|
|
if(l_iCurDivX + 1 == l_iEmptyDivX)
|
|
l_iDirection = 2;
|
|
else
|
|
if(l_iCurDivX - 1 == l_iEmptyDivX)
|
|
l_iDirection = 4;
|
|
}
|
|
}
|
|
|
|
if(l_iDirection)
|
|
{
|
|
Rect l_MoveRect = m_Cells[iIndex]->GetRect();
|
|
if(l_iDirection & 1)
|
|
l_MoveRect.OffsetVert((l_iDirection == 1) ? -m_iCellSize : m_iCellSize);
|
|
else
|
|
l_MoveRect.OffsetHorz((l_iDirection == 2) ? m_iCellSize : -m_iCellSize);
|
|
|
|
m_Cells[iIndex]->SetRect(l_MoveRect);
|
|
|
|
m_iNumberOfMoves += 1;
|
|
|
|
String l_sFormat = t_(" Moves: ");
|
|
l_sFormat += "%d";
|
|
String l_sStr(Format(l_sFormat, m_iNumberOfMoves));
|
|
if(m_iBestNumberOfMoves != -1)
|
|
{
|
|
String l_sStr2(Format(" (%d)", m_iBestNumberOfMoves));
|
|
l_sStr += l_sStr2;
|
|
}
|
|
|
|
m_StatusBar.SetLabel(l_sStr);
|
|
|
|
YSwap(m_Cells[iIndex]->m_iCurID, m_iCurEmptyID);
|
|
|
|
if(m_iCurEmptyID == m_iMaxNumberOfCells - 1)
|
|
{
|
|
vector<CYPuzzleCell*>::iterator Idx = m_Cells.begin();
|
|
for(; Idx != m_Cells.end(); Idx++)
|
|
if((*Idx)->m_iID != (*Idx)->m_iCurID)
|
|
break;
|
|
|
|
if(Idx == m_Cells.end())
|
|
{
|
|
if(m_iBestNumberOfMoves == -1 || m_iBestNumberOfMoves > m_iNumberOfMoves)
|
|
m_iBestNumberOfMoves = m_iNumberOfMoves;
|
|
|
|
PromptOK(t_("You win!"));
|
|
ShuffleVector();
|
|
ArrangeButtons(m_viShuffle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void YPuzzle::ArrangeButtons(vector<int> &viShuffle)
|
|
{
|
|
Rect l_CellRect;
|
|
CYPuzzleCell *l_pCell = NULL;
|
|
int l_iDivY = 0, l_iDivX = 0, l_iX = 0, l_iY = 0;
|
|
String l_sStr;
|
|
for(int Idx = 0; Idx < m_iMaxNumberOfCells - 1; Idx++)
|
|
{
|
|
l_iDivY = viShuffle[Idx] / m_iNumberOfCellsX;
|
|
l_iDivX = viShuffle[Idx] % m_iNumberOfCellsX;
|
|
l_iX = l_iDivX * m_iCellSize;
|
|
l_iY = l_iDivY * m_iCellSize;
|
|
l_CellRect.Set(l_iX, l_iY, l_iX + m_iCellSize, l_iY + m_iCellSize);
|
|
|
|
m_Cells[Idx]->SetRect(l_CellRect);
|
|
m_Cells[Idx]->m_iCurID = viShuffle[Idx];
|
|
}
|
|
|
|
m_iNumberOfMoves = 0;
|
|
l_sStr = t_(" Moves: ");
|
|
l_sStr += '0';
|
|
if(m_iBestNumberOfMoves != -1)
|
|
{
|
|
String l_sStr2(Format(" (%d)", m_iBestNumberOfMoves));
|
|
l_sStr += l_sStr2;
|
|
}
|
|
|
|
m_StatusBar.SetLabel(l_sStr);
|
|
}
|