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

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