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