Diffstat (limited to 'noncore/games/tictac/tictac.cpp') (more/less context) (show whitespace changes)
-rw-r--r-- | noncore/games/tictac/tictac.cpp | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/noncore/games/tictac/tictac.cpp b/noncore/games/tictac/tictac.cpp new file mode 100644 index 0000000..e887b00 --- a/dev/null +++ b/noncore/games/tictac/tictac.cpp | |||
@@ -0,0 +1,377 @@ | |||
1 | /**************************************************************************** | ||
2 | ** $Id$ | ||
3 | ** | ||
4 | ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. | ||
5 | ** | ||
6 | ** This file is part of an example program for Qt. This example | ||
7 | ** program may be used, distributed and modified without limitation. | ||
8 | ** | ||
9 | *****************************************************************************/ | ||
10 | |||
11 | #include "tictac.h" | ||
12 | #include <qpe/qpeapplication.h> | ||
13 | #include <qpainter.h> | ||
14 | #include <qdrawutil.h> | ||
15 | #include <qcombobox.h> | ||
16 | #include <qcheckbox.h> | ||
17 | #include <qlabel.h> | ||
18 | #include <qlayout.h> | ||
19 | #include <stdlib.h> // rand() function | ||
20 | #include <qdatetime.h> // seed for rand() | ||
21 | |||
22 | |||
23 | //*************************************************************************** | ||
24 | //* TicTacButton member functions | ||
25 | //*************************************************************************** | ||
26 | |||
27 | // -------------------------------------------------------------------------- | ||
28 | // Creates a TicTacButton | ||
29 | // | ||
30 | |||
31 | TicTacButton::TicTacButton( QWidget *parent ) : QPushButton( parent ) | ||
32 | { | ||
33 | t = Blank; // initial type | ||
34 | } | ||
35 | |||
36 | // -------------------------------------------------------------------------- | ||
37 | // Paints TicTacButton | ||
38 | // | ||
39 | |||
40 | void TicTacButton::drawButtonLabel( QPainter *p ) | ||
41 | { | ||
42 | QRect r = rect(); | ||
43 | p->setPen( QPen( white,2 ) ); // set fat pen | ||
44 | if ( t == Circle ) { | ||
45 | p->drawEllipse( r.left()+4, r.top()+4, r.width()-8, r.height()-8 ); | ||
46 | } else if ( t == Cross ) { // draw cross | ||
47 | p->drawLine( r.topLeft() +QPoint(4,4), r.bottomRight()-QPoint(4,4)); | ||
48 | p->drawLine( r.bottomLeft()+QPoint(4,-4),r.topRight() -QPoint(4,-4)); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | |||
53 | //*************************************************************************** | ||
54 | //* TicTacGameBoard member functions | ||
55 | //*************************************************************************** | ||
56 | |||
57 | // -------------------------------------------------------------------------- | ||
58 | // Creates a game board with N x N buttons and connects the "clicked()" | ||
59 | // signal of all buttons to the "buttonClicked()" slot. | ||
60 | // | ||
61 | |||
62 | TicTacGameBoard::TicTacGameBoard( int n, QWidget *parent, const char *name ) | ||
63 | : QWidget( parent, name ) | ||
64 | { | ||
65 | showMaximized(); | ||
66 | st = Init; // initial state | ||
67 | nBoard = n; | ||
68 | n *= n; // make square | ||
69 | comp_starts = FALSE; // human starts | ||
70 | buttons = new TicTacButtons(n); // create real buttons | ||
71 | btArray = new TicTacArray(n); // create button model | ||
72 | QGridLayout * grid = new QGridLayout( this, 3, 3, 4 ); | ||
73 | QPalette p( blue ); | ||
74 | for ( int i=0; i<n; i++ ) { // create and connect buttons | ||
75 | TicTacButton *ttb = new TicTacButton( this ); | ||
76 | ttb->setPalette( p ); | ||
77 | ttb->setEnabled( FALSE ); | ||
78 | connect( ttb, SIGNAL(clicked()), SLOT(buttonClicked()) ); | ||
79 | grid->addWidget( ttb, i%3, i/3 ); | ||
80 | buttons->insert( i, ttb ); | ||
81 | btArray->at(i) = TicTacButton::Blank;// initial button type | ||
82 | } | ||
83 | QTime t = QTime::currentTime(); // set random seed | ||
84 | srand( t.hour()*12+t.minute()*60+t.second()*60 ); | ||
85 | } | ||
86 | |||
87 | TicTacGameBoard::~TicTacGameBoard() | ||
88 | { | ||
89 | delete buttons; | ||
90 | delete btArray; | ||
91 | } | ||
92 | |||
93 | |||
94 | // -------------------------------------------------------------------------- | ||
95 | // TicTacGameBoard::computerStarts( bool v ) | ||
96 | // | ||
97 | // Computer starts if v=TRUE. The human starts by default. | ||
98 | // | ||
99 | |||
100 | void TicTacGameBoard::computerStarts( bool v ) | ||
101 | { | ||
102 | comp_starts = v; | ||
103 | } | ||
104 | |||
105 | |||
106 | // -------------------------------------------------------------------------- | ||
107 | // TicTacGameBoard::newGame() | ||
108 | // | ||
109 | // Clears the game board and prepares for a new game | ||
110 | // | ||
111 | |||
112 | void TicTacGameBoard::newGame() | ||
113 | { | ||
114 | st = HumansTurn; | ||
115 | for ( int i=0; i<nBoard*nBoard; i++ ) | ||
116 | btArray->at(i) = TicTacButton::Blank; | ||
117 | if ( comp_starts ) | ||
118 | computerMove(); | ||
119 | else | ||
120 | updateButtons(); | ||
121 | } | ||
122 | |||
123 | |||
124 | // -------------------------------------------------------------------------- | ||
125 | // TicTacGameBoard::buttonClicked() - SLOT | ||
126 | // | ||
127 | // This slot is activated when a TicTacButton emits the signal "clicked()", | ||
128 | // i.e. the user has clicked on a TicTacButton. | ||
129 | // | ||
130 | |||
131 | void TicTacGameBoard::buttonClicked() | ||
132 | { | ||
133 | if ( st != HumansTurn ) // not ready | ||
134 | return; | ||
135 | int i = buttons->findRef( (TicTacButton*)sender() ); | ||
136 | TicTacButton *b = buttons->at(i); // get piece that was pressed | ||
137 | if ( b->type() == TicTacButton::Blank ) {// empty piece? | ||
138 | btArray->at(i) = TicTacButton::Circle; | ||
139 | updateButtons(); | ||
140 | if ( checkBoard( btArray ) == 0 )// not a winning move? | ||
141 | computerMove(); | ||
142 | int s = checkBoard( btArray ); | ||
143 | if ( s ) { // any winners yet? | ||
144 | st = s == TicTacButton::Circle ? HumanWon : ComputerWon; | ||
145 | emit finished(); | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | |||
151 | // -------------------------------------------------------------------------- | ||
152 | // TicTacGameBoard::updateButtons() | ||
153 | // | ||
154 | // Updates all buttons that have changed state | ||
155 | // | ||
156 | |||
157 | void TicTacGameBoard::updateButtons() | ||
158 | { | ||
159 | for ( int i=0; i<nBoard*nBoard; i++ ) { | ||
160 | if ( buttons->at(i)->type() != btArray->at(i) ) | ||
161 | buttons->at(i)->setType( (TicTacButton::Type)btArray->at(i) ); | ||
162 | buttons->at(i)->setEnabled( buttons->at(i)->type() == | ||
163 | TicTacButton::Blank ); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | |||
168 | // -------------------------------------------------------------------------- | ||
169 | // TicTacGameBoard::checkBoard() | ||
170 | // | ||
171 | // Checks if one of the players won the game, works for any board size. | ||
172 | // | ||
173 | // Returns: | ||
174 | // - TicTacButton::Cross if the player with X buttons won | ||
175 | // - TicTacButton::Circle if the player with O buttons won | ||
176 | // - Zero (0) if there is no winner yet | ||
177 | // | ||
178 | |||
179 | int TicTacGameBoard::checkBoard( TicTacArray *a ) | ||
180 | { | ||
181 | int t = 0; | ||
182 | int row, col; | ||
183 | bool won = FALSE; | ||
184 | for ( row=0; row<nBoard && !won; row++ ) {// check horizontal | ||
185 | t = a->at(row*nBoard); | ||
186 | if ( t == TicTacButton::Blank ) | ||
187 | continue; | ||
188 | col = 1; | ||
189 | while ( col<nBoard && a->at(row*nBoard+col) == t ) | ||
190 | col++; | ||
191 | if ( col == nBoard ) | ||
192 | won = TRUE; | ||
193 | } | ||
194 | for ( col=0; col<nBoard && !won; col++ ) {// check vertical | ||
195 | t = a->at(col); | ||
196 | if ( t == TicTacButton::Blank ) | ||
197 | continue; | ||
198 | row = 1; | ||
199 | while ( row<nBoard && a->at(row*nBoard+col) == t ) | ||
200 | row++; | ||
201 | if ( row == nBoard ) | ||
202 | won = TRUE; | ||
203 | } | ||
204 | if ( !won ) { // check diagonal top left | ||
205 | t = a->at(0); // to bottom right | ||
206 | if ( t != TicTacButton::Blank ) { | ||
207 | int i = 1; | ||
208 | while ( i<nBoard && a->at(i*nBoard+i) == t ) | ||
209 | i++; | ||
210 | if ( i == nBoard ) | ||
211 | won = TRUE; | ||
212 | } | ||
213 | } | ||
214 | if ( !won ) { // check diagonal bottom left | ||
215 | int j = nBoard-1; // to top right | ||
216 | int i = 0; | ||
217 | t = a->at(i+j*nBoard); | ||
218 | if ( t != TicTacButton::Blank ) { | ||
219 | i++; j--; | ||
220 | while ( i<nBoard && a->at(i+j*nBoard) == t ) { | ||
221 | i++; j--; | ||
222 | } | ||
223 | if ( i == nBoard ) | ||
224 | won = TRUE; | ||
225 | } | ||
226 | } | ||
227 | if ( !won ) // no winner | ||
228 | t = 0; | ||
229 | return t; | ||
230 | } | ||
231 | |||
232 | |||
233 | // -------------------------------------------------------------------------- | ||
234 | // TicTacGameBoard::computerMove() | ||
235 | // | ||
236 | // Puts a piece on the game board. Very, very simple. | ||
237 | // | ||
238 | |||
239 | void TicTacGameBoard::computerMove() | ||
240 | { | ||
241 | int numButtons = nBoard*nBoard; | ||
242 | int *altv = new int[numButtons]; // buttons alternatives | ||
243 | int altc = 0; | ||
244 | int stopHuman = -1; | ||
245 | TicTacArray a = btArray->copy(); | ||
246 | int i; | ||
247 | for ( i=0; i<numButtons; i++ ) { // try all positions | ||
248 | if ( a[i] != TicTacButton::Blank )// already a piece there | ||
249 | continue; | ||
250 | a[i] = TicTacButton::Cross; // test if computer wins | ||
251 | if ( checkBoard(&a) == a[i] ) { // computer will win | ||
252 | st = ComputerWon; | ||
253 | stopHuman = -1; | ||
254 | break; | ||
255 | } | ||
256 | a[i] = TicTacButton::Circle; // test if human wins | ||
257 | if ( checkBoard(&a) == a[i] ) { // oops... | ||
258 | stopHuman = i; // remember position | ||
259 | a[i] = TicTacButton::Blank; // restore button | ||
260 | continue; // computer still might win | ||
261 | } | ||
262 | a[i] = TicTacButton::Blank; // restore button | ||
263 | altv[altc++] = i; // remember alternative | ||
264 | } | ||
265 | if ( stopHuman >= 0 ) // must stop human from winning | ||
266 | a[stopHuman] = TicTacButton::Cross; | ||
267 | else if ( i == numButtons ) { // tried all alternatives | ||
268 | if ( altc > 0 ) // set random piece | ||
269 | a[altv[rand()%(altc--)]] = TicTacButton::Cross; | ||
270 | if ( altc == 0 ) { // no more blanks | ||
271 | st = NobodyWon; | ||
272 | emit finished(); | ||
273 | } | ||
274 | } | ||
275 | *btArray = a; // update model | ||
276 | updateButtons(); // update buttons | ||
277 | delete[] altv; | ||
278 | } | ||
279 | |||
280 | |||
281 | //*************************************************************************** | ||
282 | //* TicTacToe member functions | ||
283 | //*************************************************************************** | ||
284 | |||
285 | // -------------------------------------------------------------------------- | ||
286 | // Creates a game widget with a game board and two push buttons, and connects | ||
287 | // signals of child widgets to slots. | ||
288 | // | ||
289 | |||
290 | TicTacToe::TicTacToe( int boardSize, QWidget *parent, const char *name ) | ||
291 | : QWidget( parent, name ) | ||
292 | { | ||
293 | QVBoxLayout * l = new QVBoxLayout( this, 6 ); | ||
294 | |||
295 | // Create a message label | ||
296 | |||
297 | message = new QLabel( this ); | ||
298 | message->setFrameStyle( QFrame::WinPanel | QFrame::Sunken ); | ||
299 | message->setAlignment( AlignCenter ); | ||
300 | l->addWidget( message ); | ||
301 | |||
302 | // Create the game board and connect the signal finished() to this | ||
303 | // gameOver() slot | ||
304 | |||
305 | board = new TicTacGameBoard( boardSize, this ); | ||
306 | connect( board, SIGNAL(finished()), SLOT(gameOver()) ); | ||
307 | l->addWidget( board ); | ||
308 | |||
309 | // Create a horizontal frame line | ||
310 | |||
311 | QFrame *line = new QFrame( this ); | ||
312 | line->setFrameStyle( QFrame::HLine | QFrame::Sunken ); | ||
313 | l->addWidget( line ); | ||
314 | |||
315 | // Create the combo box for deciding who should start, and | ||
316 | // connect its clicked() signals to the buttonClicked() slot | ||
317 | |||
318 | whoStarts = new QComboBox( this ); | ||
319 | whoStarts->insertItem( "Computer starts" ); | ||
320 | whoStarts->insertItem( "Human starts" ); | ||
321 | l->addWidget( whoStarts ); | ||
322 | |||
323 | // Create the push buttons and connect their clicked() signals | ||
324 | // to this right slots. | ||
325 | |||
326 | newGame = new QPushButton( "Play!", this ); | ||
327 | connect( newGame, SIGNAL(clicked()), SLOT(newGameClicked()) ); | ||
328 | quit = new QPushButton( "Quit", this ); | ||
329 | connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) ); | ||
330 | QHBoxLayout * b = new QHBoxLayout; | ||
331 | l->addLayout( b ); | ||
332 | b->addWidget( newGame ); | ||
333 | b->addWidget( quit ); | ||
334 | showMaximized(); | ||
335 | newState(); | ||
336 | } | ||
337 | |||
338 | |||
339 | // -------------------------------------------------------------------------- | ||
340 | // TicTacToe::newGameClicked() - SLOT | ||
341 | // | ||
342 | // This slot is activated when the new game button is clicked. | ||
343 | // | ||
344 | |||
345 | void TicTacToe::newGameClicked() | ||
346 | { | ||
347 | board->computerStarts( whoStarts->currentItem() == 0 ); | ||
348 | board->newGame(); | ||
349 | newState(); | ||
350 | } | ||
351 | |||
352 | |||
353 | // -------------------------------------------------------------------------- | ||
354 | // TicTacToe::gameOver() - SLOT | ||
355 | // | ||
356 | // This slot is activated when the TicTacGameBoard emits the signal | ||
357 | // "finished()", i.e. when a player has won or when it is a draw. | ||
358 | // | ||
359 | |||
360 | void TicTacToe::gameOver() | ||
361 | { | ||
362 | newState(); // update text box | ||
363 | } | ||
364 | |||
365 | |||
366 | // -------------------------------------------------------------------------- | ||
367 | // Updates the message to reflect a new state. | ||
368 | // | ||
369 | |||
370 | void TicTacToe::newState() | ||
371 | { | ||
372 | static const char *msg[] = { // TicTacGameBoard::State texts | ||
373 | "Click Play to start", "Make your move", | ||
374 | "You won!", "Computer won!", "It's a draw" }; | ||
375 | message->setText( msg[board->state()] ); | ||
376 | return; | ||
377 | } | ||