source: trunk/MagicSoft/Mars/mtools/MineSweeper.cc@ 1578

Last change on this file since 1578 was 1435, checked in by tbretz, 22 years ago
*** empty log message ***
File size: 12.5 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz 07/2002 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2002
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MineSweeper
28// -----------
29//
30// Camera Display Games: Mine Sweeper
31//
32// Start the game by:
33// MineSweeper mine;
34//
35// It is the well known Mine Sweeper.
36// Set a mark using a single mouse click.
37// Open a pixel using a double click.
38//
39// Try to open all pixels without bombs. If you open a pixel with no
40// bomb around all pixels around are opened.
41//
42// To restart the game use the context menu. It can only be accessed if
43// the game has been stopped (either because you win the game or because
44// you hit a bomb) With the context menu you can also toggle between
45// different camera layouts.
46//
47////////////////////////////////////////////////////////////////////////////
48#include "MineSweeper.h"
49
50#include <iostream.h>
51
52#include <TText.h>
53#include <TMarker.h>
54#include <TRandom.h>
55#include <TCanvas.h>
56#include <TClonesArray.h>
57#include <TInterpreter.h>
58
59#include "MHexagon.h"
60
61#include "MGeomPix.h"
62#include "MGeomCamCT1.h"
63#include "MGeomCamMagic.h"
64
65ClassImp(MineSweeper);
66
67const Int_t MineSweeper::fColorBombs[7] = {
68 22,
69 kYellow,
70 kGreen,
71 kBlue,
72 kCyan,
73 kMagenta,
74 kRed
75};
76
77void MineSweeper::Free()
78{
79 if (!fGeomCam)
80 return;
81
82 fPixels->Delete();
83 fText->Delete();
84 fFlags->Delete();
85
86 delete fText;
87 delete fFlags;
88 delete fPixels;
89
90 delete fGeomCam;
91}
92
93void MineSweeper::ChangeCamera()
94{
95 static Bool_t ct1=kFALSE;
96
97 cout << "Change to " << (ct1?"Magic":"CT1") << endl;
98
99 if (ct1)
100 SetNewCamera(new MGeomCamMagic);
101 else
102 SetNewCamera(new MGeomCamCT1);
103
104 ct1 = !ct1;
105
106 Reset();
107 DrawHexagons();
108}
109
110void MineSweeper::SetNewCamera(MGeomCam *geom)
111{
112 Free();
113
114 //
115 // Reset the display geometry
116 //
117 fW=0;
118 fH=0;
119
120 //
121 // Set new camera
122 //
123 fGeomCam = geom;
124
125 //
126 // create the hexagons of the display
127 //
128 fNumPixels = fGeomCam->GetNumPixels();
129 fRange = fGeomCam->GetMaxRadius();
130
131 //
132 // Construct all hexagons. Use new-operator with placement
133 //
134 fNumBombs = fNumPixels/5;
135
136 fText = new TClonesArray("TText", fNumPixels);
137 fFlags = new TClonesArray("TMarker", fNumPixels);
138 fPixels = new TClonesArray("MHexagon", fNumPixels);
139
140 for (UInt_t i=0; i<fNumPixels; i++)
141 {
142 MHexagon &h = *new ((*fPixels)[i]) MHexagon((*fGeomCam)[i]);
143#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
144 h.SetBit(kNoContextMenu|kCannotPick);
145#endif
146
147 TText &t = *new ((*fText)[i]) TText;
148 t.SetTextFont(122);
149 t.SetTextAlign(22); // centered/centered
150 t.SetTextSize(0.3*h.GetD()/fRange);
151#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
152 t.SetBit(kNoContextMenu|kCannotPick);
153#endif
154
155 const MGeomPix &pix = (*fGeomCam)[i];
156
157 TMarker &m = *new ((*fFlags)[i]) TMarker(pix.GetX(), pix.GetY(), kOpenStar);
158#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
159 m.SetBit(kNoContextMenu|kCannotPick);
160#endif
161 }
162}
163
164// ------------------------------------------------------------------------
165//
166// Draw all pixels of the camera
167// (means apend all pixelobjects to the current pad)
168//
169void MineSweeper::DrawHexagons()
170{
171 for (UInt_t i=0; i<fNumPixels; i++)
172 (*this)[i].Draw();
173}
174
175// ------------------------------------------------------------------------
176//
177// default constructor
178//
179MineSweeper::MineSweeper()
180 : fGeomCam(NULL), fDone(NULL), fShow(NULL), fW(0), fH(0), fDrawingPad(NULL), fIsAllocated(kFALSE)
181{
182 SetNewCamera(new MGeomCamMagic);
183
184 //
185 // Make sure, that the object is destroyed when the canvas/pad is
186 // destroyed. Make also sure, that the interpreter doesn't try to
187 // delete it a second time.
188 //
189 SetBit(kCanDelete);
190 gInterpreter->DeleteGlobal(this);
191
192 Draw();
193}
194
195// ------------------------------------------------------------------------
196//
197// Destructor. Deletes TClonesArrays for hexagons and legend elements.
198//
199MineSweeper::~MineSweeper()
200{
201 Free();
202
203 delete fShow;
204
205 if (fDone)
206 delete fDone;
207
208 if (fDrawingPad->GetListOfPrimitives()->FindObject(this)==this)
209 {
210 fDrawingPad->RecursiveRemove(this);
211 delete fDrawingPad;
212 }
213}
214
215// ------------------------------------------------------------------------
216//
217// This is called at any time the canvas should get repainted.
218// Here we maintain an aspect ratio of 5/4=1.15. This makes sure,
219// that the camera image doesn't get distorted by resizing the canvas.
220//
221void MineSweeper::Paint(Option_t *opt)
222{
223 const UInt_t w = (UInt_t)(gPad->GetWw()*gPad->GetAbsWNDC());
224 const UInt_t h = (UInt_t)(gPad->GetWh()*gPad->GetAbsHNDC());
225
226 //
227 // Check for a change in width or height, and make sure, that the
228 // first call also sets the range
229 //
230 if (w*fH == h*fW && fW && fH)
231 return;
232
233 //
234 // Calculate aspect ratio (5/4=1.25 recommended)
235 //
236 const Double_t ratio = (Double_t)w/h;
237
238 Float_t x;
239 Float_t y;
240
241 if (ratio>1.0)
242 {
243 x = fRange*(ratio*2-1);
244 y = fRange;
245 }
246 else
247 {
248 x = fRange;
249 y = fRange/ratio;
250 }
251
252 fH = h;
253 fW = w;
254
255 //
256 // Set new range
257 //
258 fDrawingPad->Range(-fRange, -y, x, y);
259
260 //
261 // Adopt absolute sized of markers to relative range
262 //
263 for (UInt_t i=0; i<fNumPixels; i++)
264 {
265 Float_t r = (*this)[i].GetD()*gPad->XtoAbsPixel(1)/325;
266 GetFlag(i)->SetMarkerSize(20.0*r/fRange);
267 }
268}
269
270// ------------------------------------------------------------------------
271//
272// Call this function to draw the camera layout into your canvas.
273// Setup a drawing canvas. Add this object and all child objects
274// (hexagons, etc) to the current pad. If no pad exists a new one is
275// created.
276//
277void MineSweeper::Draw(Option_t *option)
278{
279 // root 3.02:
280 // gPad->SetFixedAspectRatio()
281
282 if (fDrawingPad)
283 return;
284
285 //
286 // if no canvas is yet existing to draw into, create a new one
287 //
288 if (!gPad)
289 {
290 /*TCanvas *c =*/ new TCanvas("MineSweeper", "Magic Mine Sweeper", 0, 0, 800, 800);
291 //c->ToggleEventStatus();
292 fIsAllocated = kTRUE;
293 }
294 else
295 fIsAllocated = kFALSE;
296
297 fDrawingPad = gPad;
298 fDrawingPad->SetBorderMode(0);
299
300 //
301 // Append this object, so that the aspect ratio is maintained
302 // (Paint-function is called)
303 //
304 AppendPad(option);
305
306 //
307 // Draw the title text
308 //
309 fShow = new TText;
310 fShow->SetTextAlign(23); // centered/bottom
311#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
312 fShow->SetBit(kNoContextMenu|kCannotPick);
313#endif
314 fShow->Draw();
315 //
316 // Reset the game pad
317 //
318 Reset();
319 DrawHexagons();
320}
321
322void MineSweeper::Update(Int_t num)
323{
324 TString txt = "Pixels: ";
325 txt += fNumPixels;
326 txt += " Bombs: ";
327 txt += num;
328
329 fShow->SetText(0, fRange, txt);
330}
331
332// ------------------------------------------------------------------------
333//
334// reset the all pixel colors to a default value
335//
336void MineSweeper::Reset()
337{
338 if (fDone)
339 {
340 delete fDone;
341 fDone = NULL;
342 }
343
344 for (UInt_t i=0; i<fNumPixels; i++)
345 {
346 Remove(GetText(i));
347 Remove(GetFlag(i));
348
349 (*this)[i].SetFillColor(kHidden);
350 (*fGeomCam)[i].ResetBit(kUserBits);
351
352 GetFlag(i)->SetMarkerColor(kBlack);
353 }
354 Update(fNumBombs);
355
356 TRandom rnd(0);
357 for (int i=0; i<fNumBombs; i++)
358 {
359 Int_t idx;
360
361 do idx = (Int_t)rnd.Uniform(fNumPixels);
362 while ((*fGeomCam)[idx].TestBit(kHasBomb));
363
364 (*fGeomCam)[idx].SetBit(kHasBomb);
365 }
366
367 fDrawingPad->SetFillColor(22);
368
369#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
370 fDrawingPad->SetBit(kNoContextMenu);
371 SetBit(kNoContextMenu);
372#endif
373}
374
375void MineSweeper::Done(TString txt, Int_t col)
376{
377 for (unsigned int j=0; j<fNumPixels; j++)
378 if ((*fGeomCam)[j].TestBit(kHasBomb))
379 {
380 (*this)[j].SetFillColor(kBlack);
381 GetFlag(j)->SetMarkerColor(kWhite);
382 }
383
384 fDone = new TText(0, 0, txt);
385 fDone->SetTextColor(kWhite); // white
386 fDone->SetTextAlign(22); // centered/centered
387 fDone->SetTextSize(0.05); // white
388#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
389 fDone->SetBit(kNoContextMenu|kCannotPick);
390#endif
391 fDone->Draw();
392
393 fDrawingPad->SetFillColor(col);
394
395#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
396 fDrawingPad->ResetBit(kNoContextMenu);
397 ResetBit(kNoContextMenu);
398#endif
399}
400
401// ------------------------------------------------------------------------
402//
403// Check whether a hexagon should be opened and which color/number should
404// be visible
405//
406void MineSweeper::OpenHexagon(Int_t idx)
407{
408 MGeomPix &pix=(*fGeomCam)[idx];
409
410 if (pix.TestBit(kIsVisible))
411 return;
412
413 if (pix.TestBit(kHasFlag))
414 Remove(GetFlag(idx));
415
416 pix.SetBit(kIsVisible);
417 pix.ResetBit(kHasFlag);
418
419 Int_t cnt=0;
420 for (int j=0; j<pix.GetNumNeighbors(); j++)
421 if ((*fGeomCam)[pix.GetNeighbor(j)].TestBit(kHasBomb))
422 cnt++;
423
424 (*this)[idx].SetFillColor(fColorBombs[cnt]);
425
426 if (cnt)
427 {
428 TText *txt = GetText(idx);
429 TString str;
430 str += cnt;
431 txt->SetText(pix.GetX(), pix.GetY(), str);
432 txt->Draw();
433 return;
434 }
435
436 for (int j=0; j<pix.GetNumNeighbors(); j++)
437 OpenHexagon(pix.GetNeighbor(j));
438}
439
440void MineSweeper::Remove(TObject *obj)
441{
442 fDrawingPad->RecursiveRemove(obj);
443}
444
445// ------------------------------------------------------------------------
446//
447// Execute a mouse event on the camera
448//
449void MineSweeper::ExecuteEvent(Int_t event, Int_t px, Int_t py)
450{
451 if (event==kMouseMotion || event==kMouseEnter || event==kMouseLeave ||
452 event==kButton1Up || event==kButton2Up || event==kButton3Up ||
453 event==kButton1Motion || event==kButton2Motion || event==kButton3Motion ||
454 event==kButton2Double || event==kButton3Double ||
455 fDone)
456 return;
457
458 /*
459 if (event==kKeyPress && py==0x1000)
460 {
461 Reset();
462 return;
463 }
464 */
465
466 UInt_t idx;
467 for (idx=0; idx<fNumPixels; idx++)
468 if ((*fPixels)[idx]->DistancetoPrimitive(px, py)==0)
469 break;
470
471 if (idx==fNumPixels)
472 return;
473
474 MGeomPix &pix=(*fGeomCam)[idx];
475
476 if (event==kButton1Double)
477 {
478 OpenHexagon(idx);
479
480 if (pix.TestBit(kHasBomb))
481 Done("Argh... you hit the Bomb!!!", kRed);
482 }
483
484 if (event==kButton1Down && !pix.TestBit(kIsVisible))
485 {
486 if (pix.TestBit(kHasFlag))
487 Remove(GetFlag(idx));
488 else
489 GetFlag(idx)->Draw();
490
491 pix.InvertBit(kHasFlag);
492 }
493
494 UInt_t vis=fNumBombs;
495 UInt_t flg=fNumBombs;
496 for (UInt_t i=0; i<fNumPixels; i++)
497 {
498 if ((*fGeomCam)[i].TestBit(kIsVisible))
499 vis++;
500 if ((*fGeomCam)[i].TestBit(kHasFlag))
501 flg--;
502 }
503
504 Update(flg);
505
506 if (vis==fNumPixels && !fDone)
507 Done("Great! Congratulations, you did it!", kGreen);
508
509 fDrawingPad->Modified();
510
511 /*
512 switch (event)
513 {
514 case kNoEvent: cout << "No Event" << endl; break;
515 case kButton1Down: cout << "Button 1 down" << endl; break;
516 case kButton2Down: cout << "Button 2 down" << endl; break;
517 case kButton3Down: cout << "Button 3 down" << endl; break;
518 case kKeyDown: cout << "Key down" << endl; break;
519 case kKeyUp: cout << "Key up" << endl; break;
520 case kKeyPress: cout << "Key press" << endl; break;
521 case kButton1Locate: cout << "Button 1 locate" << endl; break;
522 case kButton2Locate: cout << "Button 2 locate" << endl; break;
523 case kButton3Locate: cout << "Button 3 locate" << endl; break;
524 }
525 */
526}
Note: See TracBrowser for help on using the repository browser.