1 /***********************************************************************
2 *
3 * Copyright (C) 2008, 2009, 2012, 2014, 2015 Graeme Gott <graeme@gottcode.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***********************************************************************/
19
20 #include "scores.h"
21
22 #include <QDialogButtonBox>
23 #include <QHeaderView>
24 #include <QInputDialog>
25 #include <QLabel>
26 #include <QScrollBar>
27 #include <QSettings>
28 #include <QTime>
29 #include <QTreeWidget>
30 #include <QVBoxLayout>
31
32 #if defined(Q_OS_UNIX)
33 #include <pwd.h>
34 #include <unistd.h>
35
36 #elif defined(Q_OS_WIN)
37 #include <lmcons.h>
38 #include <windows.h>
39
40 #endif
41
42 namespace {
43 // ============================================================================
44
45 class Score : public QTreeWidgetItem
46 {
47 public:
48 Score(int seconds, int steps, int algorithm, int size);
49
operator <(const QTreeWidgetItem & other) const50 bool operator<(const QTreeWidgetItem& other) const
51 {
52 return data(1, Qt::UserRole).toInt() < other.data(1, Qt::UserRole).toInt();
53 }
54 };
55
56 // ============================================================================
57
Score(int seconds,int steps,int algorithm,int size)58 Score::Score(int seconds, int steps, int algorithm, int size)
59 : QTreeWidgetItem(QTreeWidgetItem::UserType + 1)
60 {
61 int score = seconds ? ((steps * size) / seconds) : (steps * size);
62 setText(1, QString::number(score));
63 setData(1, Qt::UserRole, score);
64 setText(2, QTime(0, 0, 0).addSecs(seconds).toString("hh:mm:ss"));
65 setData(2, Qt::UserRole, QString::number(seconds));
66 setText(3, QString::number(steps));
67 setData(4, Qt::UserRole, QString::number(algorithm));
68 setText(5, QString::number(size));
69
70 setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
71 setTextAlignment(2, Qt::AlignRight | Qt::AlignVCenter);
72 setTextAlignment(3, Qt::AlignRight | Qt::AlignVCenter);
73 setTextAlignment(4, Qt::AlignCenter | Qt::AlignVCenter);
74 setTextAlignment(5, Qt::AlignRight | Qt::AlignVCenter);
75
76 static QStringList algorithms = QStringList()
77 << QLabel::tr("Hunt and Kill")
78 << QLabel::tr("Kruskal")
79 << QLabel::tr("Prim")
80 << QLabel::tr("Recursive Backtracker")
81 << QLabel::tr("Stack")
82 << QLabel::tr("Stack 2")
83 << QLabel::tr("Stack 3")
84 << QLabel::tr("Stack 4")
85 << QLabel::tr("Stack 5");
86 Q_ASSERT(algorithm > -1);
87 Q_ASSERT(algorithm < algorithms.size());
88 setText(4, algorithms.at(algorithm));
89 }
90
91 // ============================================================================
92 }
93
94 // ============================================================================
95
Scores(QWidget * parent)96 Scores::Scores(QWidget* parent)
97 : QDialog(parent)
98 {
99 setWindowTitle(tr("CuteMaze Scores"));
100
101 QVBoxLayout* layout = new QVBoxLayout(this);
102
103 m_board = new QTreeWidget(this);
104 m_board->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
105 m_board->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
106 m_board->setRootIsDecorated(false);
107 m_board->setColumnCount(5);
108 m_board->setHeaderLabels(QStringList() << tr("Name") << tr("Score") << tr("Time") << tr("Steps") << tr("Algorithm") << tr("Size"));
109 m_board->header()->setStretchLastSection(false);
110 m_board->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
111 layout->addWidget(m_board);
112
113 layout->addSpacing(12);
114
115 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, this);
116 connect(buttons, &QDialogButtonBox::rejected, this, &Scores::reject);
117 layout->addWidget(buttons);
118
119 read();
120 }
121
122 // ============================================================================
123
addScore(int steps,int seconds,int algorithm,int size)124 void Scores::addScore(int steps, int seconds, int algorithm, int size)
125 {
126 // Check if it is a high score
127 int value = seconds ? ((steps * size) / seconds) : (steps * size);
128 int count = m_board->topLevelItemCount();
129 Q_ASSERT(count < 11);
130 if (count == 10 && value < m_board->topLevelItem(count - 1)->text(1).toInt()) {
131 return;
132 }
133
134 // Get player's name
135 QString name;
136 #if defined(Q_OS_UNIX)
137 {
138 passwd* pws = getpwuid(geteuid());
139 if (pws) {
140 name = pws->pw_gecos;
141 if (name.isEmpty()) {
142 name = pws->pw_name;
143 }
144 }
145 }
146 #elif defined(Q_OS_WIN)
147 {
148 WCHAR buffer[UNLEN + 1];
149 DWORD count = sizeof(buffer);
150 if (GetUserName(buffer, &count)) {
151 name = QString::fromStdWString(buffer);
152 }
153 }
154 #endif
155 bool ok = true;
156 name = QInputDialog::getText(parentWidget(), tr("Congratulations!"), tr("Your score has made the top ten.\nPlease enter your name:"), QLineEdit::Normal, name, &ok);
157 if (!ok || name.isEmpty()) {
158 return;
159 }
160
161 // Create score
162 QTreeWidgetItem* score = new Score(seconds, steps, algorithm, size);
163 score->setText(0, name);
164 m_board->addTopLevelItem(score);
165 m_board->clearSelection();
166 score->setSelected(true);
167 updateItems();
168
169 // Save items
170 QStringList values;
171 QTreeWidgetItem* item;
172 count = m_board->topLevelItemCount();
173 for (int i = 0; i < count; ++i) {
174 item = m_board->topLevelItem(i);
175 values += QString("%1:%2:%3:%4:%5") .arg(item->text(0)) .arg(item->data(2, Qt::UserRole).toInt()) .arg(item->text(3)) .arg(item->data(4, Qt::UserRole).toInt()) .arg(item->text(5).toInt());
176 }
177 QSettings().setValue("Scores", values);
178
179 show();
180 }
181
182 // ============================================================================
183
read()184 void Scores::read()
185 {
186 QStringList data = QSettings().value("Scores").toStringList();
187 for (const QString& s : data) {
188 QStringList values = s.split(':');
189 if (values.size() != 5) {
190 continue;
191 }
192 Score* score = new Score(values[1].toInt(), values[2].toInt(), values[3].toInt(), values[4].toInt());
193 score->setText(0, values[0]);
194 m_board->addTopLevelItem(score);
195 }
196 updateItems();
197 }
198
199 // ============================================================================
200
updateItems()201 void Scores::updateItems()
202 {
203 // Sort items
204 m_board->sortItems(1, Qt::DescendingOrder);
205 while (m_board->topLevelItemCount() > 10) {
206 m_board->takeTopLevelItem(10);
207 }
208
209 // Find minimum size
210 int frame = m_board->frameWidth() * 2;
211 int width = frame;
212 for (int i = 0; i < 6; ++i) {
213 width += m_board->columnWidth(i);
214 }
215 m_board->setMinimumSize(width, frame + m_board->header()->height() + m_board->sizeHintForRow(0) * 10);
216 }
217
218 // ============================================================================
219