1 /***********************************************************************
2 *
3 * Copyright (C) 2009, 2013, 2014 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 "board.h"
21
22 #include "cell.h"
23 #include "letter.h"
24 #include "pattern.h"
25 #include "word.h"
26
27 #include <QLocale>
28 #include <QSettings>
29 #include <QTimer>
30
Board(QObject * parent)31 Board::Board(QObject* parent)
32 : QGraphicsScene(parent), m_pattern(0), m_current_word(0), m_hint(0), m_finished(true), m_paused(false) {
33 QTimer* auto_save = new QTimer(this);
34 auto_save->setInterval(30000);
35 connect(auto_save, &QTimer::timeout, this, &Board::saveGame);
36 auto_save->start();
37
38 m_wordlist = new WordList(this);
39 }
40
41 //-----------------------------------------------------------------------------
42
~Board()43 Board::~Board() {
44 cleanUp();
45 }
46
47 //-----------------------------------------------------------------------------
48
check(const QString & original_word,const QString & current_word)49 void Board::check(const QString& original_word, const QString& current_word) {
50 emit wordSolved(original_word, current_word);
51 m_finished = true;
52 for (Word* word : m_words) {
53 m_finished &= word->isCorrect();
54 }
55 if (m_finished) {
56 QSettings().remove("Current/Words");
57 emit finished();
58 }
59 }
60
61 //-----------------------------------------------------------------------------
62
click(const QString & word)63 void Board::click(const QString& word) {
64 emit wordSelected(word);
65 }
66
67 //-----------------------------------------------------------------------------
68
setCurrentWord(Word * word)69 void Board::setCurrentWord(Word* word) {
70 if (m_current_word) {
71 m_current_word->setHighlight(false);
72 }
73 m_current_word = !m_paused ? word : 0;
74 if (m_current_word) {
75 m_current_word->setHighlight(true);
76 }
77 delete m_hint;
78 m_hint = 0;
79 emit hintAvailable(m_current_word != 0);
80 }
81
82 //-----------------------------------------------------------------------------
83
setPaused(bool paused)84 void Board::setPaused(bool paused) {
85 if (m_finished) {
86 return;
87 }
88
89 m_paused = paused;
90 for (int x = 0; x < m_cells.count(); ++x) {
91 QList<Cell*>& cells = m_cells[x];
92 for (int y = 0; y < cells.count(); ++y) {
93 Cell* cell = cells[y];
94 if (cell) {
95 cell->letter()->setPaused(paused);
96 }
97 }
98 }
99 if (m_paused) {
100 setCurrentWord(0);
101 }
102 emit pauseChanged();
103 }
104
105 //-----------------------------------------------------------------------------
106
openGame()107 void Board::openGame() {
108 cleanUp();
109 emit loading();
110
111 QSettings settings;
112 m_wordlist->setLanguage(settings.value("Current/Language", WordList::defaultLanguage()).toString());
113 m_pattern = Pattern::create(m_wordlist, settings.value("Current/Pattern").toInt());
114 m_pattern->setCount(settings.value("Current/Count").toInt());
115 m_pattern->setLength(settings.value("Current/Length").toInt());
116 m_pattern->setSeed(settings.value("Current/Seed").toUInt());
117
118 connect(m_pattern, &Pattern::generated, this, &Board::patternGenerated);
119 m_pattern->start();
120 }
121
122 //-----------------------------------------------------------------------------
123
openGame(const QString & number)124 bool Board::openGame(const QString& number) {
125 if (!number.startsWith("4")) {
126 return false;
127 }
128
129 // Parse language
130 int index = 1;
131 for (int i = 1; i < number.length(); ++i) {
132 if (!number.at(i).isDigit()) {
133 index = i;
134 } else {
135 break;
136 }
137 }
138 QString language = WordList::defaultLanguage();
139 if (index > 1) {
140 language = number.mid(1, index);
141 index += 1;
142 }
143
144 // Parse type
145 if (index == number.length()) {
146 return false;
147 }
148 int pattern = number.at(index).digitValue();
149
150 // Parse count
151 if (++index == number.length()) {
152 return false;
153 }
154 int count = number.at(index).digitValue();
155
156 // Parse length
157 if (++index == number.length()) {
158 return false;
159 }
160 bool ok = false;
161 int length = number.mid(index, 2).toInt(&ok, 16) + 5;
162 if (!ok) {
163 return false;
164 }
165
166 // Parse seed
167 if ((index += 2) == number.length()) {
168 return false;
169 }
170 unsigned int seed = number.mid(index).toUInt(&ok, 16);
171 if (!ok) {
172 return false;
173 }
174
175 // Start game
176 cleanUp();
177 emit loading();
178
179 m_wordlist->setLanguage(language);
180 m_pattern = Pattern::create(m_wordlist, pattern);
181 m_pattern->setCount(count);
182 m_pattern->setLength(length);
183 m_pattern->setSeed(seed);
184
185 connect(m_pattern, &Pattern::generated, this, &Board::patternGenerated);
186 m_pattern->start();
187
188 return true;
189 }
190
191 //-----------------------------------------------------------------------------
192
saveGame()193 void Board::saveGame() {
194 if (!m_finished && !m_words.isEmpty()) {
195 QStringList words;
196 for (Word* word : m_words) {
197 words.append(word->toString());
198 }
199 QSettings().setValue("Current/Words", words);
200 }
201 }
202
203 //-----------------------------------------------------------------------------
204
showHint()205 void Board::showHint() {
206 if (m_current_word && !mouseGrabberItem()) {
207 delete m_hint;
208 m_hint = m_current_word->hint();
209 }
210 }
211
212 //-----------------------------------------------------------------------------
213
togglePaused()214 void Board::togglePaused() {
215 setPaused(!m_paused);
216 }
217
218 //-----------------------------------------------------------------------------
219
patternGenerated()220 void Board::patternGenerated() {
221 m_words = m_pattern->solution();
222 QSize size = m_pattern->size();
223 setSceneRect(0, 0, size.width() * 34 + 2, size.height() * 34 + 34);
224
225 for (int x = 0; x < size.width(); ++x) {
226 QList<Cell*> cells;
227 for (int y = 0; y < size.height(); ++y) {
228 cells.append(0);
229 }
230 m_cells.append(cells);
231 }
232
233 for (Word* word : m_words) {
234 word->setBoard(this);
235 QList<QPoint> positions = word->positions();
236 for (int i = 0; i < positions.count(); ++i) {
237 const QPoint& pos = positions.at(i);
238 Cell* cell = m_cells.at(pos.x()).at(pos.y());
239 if (cell == 0) {
240 Letter* letter = new Letter(word->at(i), this);
241 addItem(letter);
242
243 cell = new Cell(pos, letter);
244 cell->setWord(word);
245 m_cells[pos.x()][pos.y()] = cell;
246 } else {
247 cell->letter()->setJoin();
248 cell->setWord(0);
249 }
250 }
251 }
252
253 for (Word* word : m_words) {
254 emit wordAdded(word->toString());
255 word->shuffle(m_pattern->words());
256 }
257
258 QStringList previous = QSettings().value("Current/Words").toStringList();
259 if (previous.count() == m_words.count()) {
260 for (int i = 0; i < m_words.count(); ++i) {
261 m_words[i]->fromString(previous.at(i));
262 }
263 }
264 saveGame();
265
266 emit started();
267 }
268
269 //-----------------------------------------------------------------------------
270
cleanUp()271 void Board::cleanUp() {
272 delete m_pattern;
273 m_pattern = 0;
274 clear();
275 for (int i = 0; i < m_cells.count(); ++i) {
276 qDeleteAll(m_cells[i]);
277 }
278 m_cells.clear();
279 m_words.clear();
280 m_current_word = 0;
281 m_hint = 0;
282 m_finished = false;
283 m_paused = false;
284 }
285