source: trunk/MagicSoft/Mars/mtools/MagicSnake.cc@ 3851

Last change on this file since 3851 was 2173, checked in by tbretz, 21 years ago
*** empty log message ***
File size: 14.1 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// MagicSnake
28// ----------
29//
30// Camera Display Games: Snake
31//
32// Start the game by:
33// MagicSnake snake;
34//
35// Controll the worm using right/left. Make sure, that the mouse pointer
36// is inside the canvas.
37//
38// Move the mouse pointer outside the canvas to pause the game.
39//
40// The pixel colors have the following meaning:
41// --------------------------------------------
42// Green: Food, collect all packages.
43// Red: Bombs, don't touch it!
44// Yellow: Transport, try it.
45// Magenta: Home, touch it to win the game.
46//
47// To restart the game use the context menu. It can only be accessed if
48// the game has been stopped (either because you win the game or because
49// you hit a bomb) With the context menu you can also toggle between
50// different camera layouts.
51//
52////////////////////////////////////////////////////////////////////////////
53#include "MagicSnake.h"
54
55#include <iostream>
56
57#include <KeySymbols.h>
58
59#include <TColor.h>
60#include <TCanvas.h>
61#include <TMarker.h>
62#include <TRandom.h>
63#include <TInterpreter.h>
64
65#include "MHexagon.h"
66
67#include "MGeomPix.h"
68#include "MGeomCamCT1.h"
69#include "MGeomCamMagic.h"
70
71ClassImp(MagicSnake);
72
73using namespace std;
74
75void MagicSnake::Free()
76{
77 if (!fGeomCam)
78 return;
79
80 fPixels->Delete();
81
82 delete fPixels;
83
84 delete fGeomCam;
85
86 delete fArray;
87}
88
89// ------------------------------------------------------------------------
90//
91// Draw all pixels of the camera
92// (means apend all pixelobjects to the current pad)
93//
94void MagicSnake::DrawHexagons()
95{
96 for (UInt_t i=0; i<fNumPixels; i++)
97 (*this)[i].Draw();
98}
99
100void MagicSnake::ChangeCamera()
101{
102 if (!fDone)
103 Done("Changing Camera...", 22);
104
105 static Bool_t ct1=kFALSE;
106
107 cout << "Change to " << (ct1?"Magic":"CT1") << endl;
108
109 if (ct1)
110 SetNewCamera(new MGeomCamMagic);
111 else
112 SetNewCamera(new MGeomCamCT1);
113
114 ct1 = !ct1;
115
116 Reset();
117 DrawHexagons();
118}
119
120void MagicSnake::SetNewCamera(MGeomCam *geom)
121{
122 Free();
123
124 //
125 // Reset the display geometry
126 //
127 fW=0;
128 fH=0;
129
130 //
131 // Set new camera
132 //
133 fGeomCam = geom;
134
135 //
136 // create the hexagons of the display
137 //
138 fNumPixels = fGeomCam->GetNumPixels();
139 fRange = fGeomCam->GetMaxRadius();
140
141 //
142 // Construct all hexagons. Use new-operator with placement
143 //
144 fPixels = new TClonesArray("MHexagon", fNumPixels);
145
146 for (UInt_t i=0; i<fNumPixels; i++)
147 {
148 MHexagon &h = *new ((*fPixels)[i]) MHexagon((*fGeomCam)[i]);
149#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
150 h.SetBit(kNoContextMenu|kCannotPick);
151#endif
152 }
153}
154
155// ------------------------------------------------------------------------
156//
157// default constructor
158//
159MagicSnake::MagicSnake()
160 : fTimer(this, 500, kTRUE), fGeomCam(NULL), fDone(NULL), fPaused(NULL), fW(0), fH(0)
161{
162 SetNewCamera(new MGeomCamMagic);
163
164 //
165 // Make sure, that the object is destroyed when the canvas/pad is
166 // destroyed. Make also sure, that the interpreter doesn't try to
167 // delete it a second time.
168 //
169 SetBit(kCanDelete);
170 gInterpreter->DeleteGlobal(this);
171
172 Draw();
173
174 Pause();
175
176 fTimer.TurnOn();
177}
178
179void MagicSnake::Pause(Bool_t yes)
180{
181 if (yes && !fPaused)
182 {
183 fPaused = new TText(0, 0, "Paused!");
184 fPaused->SetTextColor(kWhite);
185 fPaused->SetTextAlign(22);
186 fPaused->SetTextSize(0.05); // white
187 fPaused->Draw();
188#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
189 fPaused->SetBit(kNoContextMenu|kCannotPick);
190 fDrawingPad->ResetBit(kNoContextMenu);
191 ResetBit(kNoContextMenu);
192#endif
193 fDrawingPad->Update();
194 }
195
196 if (yes)
197 return;
198
199 if (!fDone)
200 {
201#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
202 fDrawingPad->SetBit(kNoContextMenu);
203 SetBit(kNoContextMenu);
204#endif
205 }
206 if (!fPaused)
207 return;
208
209 Remove(fPaused);
210
211 fDrawingPad->Update();
212
213 delete fPaused;
214 fPaused=NULL;
215}
216
217// ------------------------------------------------------------------------
218//
219// Destructor. Deletes TClonesArrays for hexagons and legend elements.
220//
221MagicSnake::~MagicSnake()
222{
223 Free();
224 Pause(kFALSE);
225
226 if (fDone)
227 delete fDone;
228
229 if (fDrawingPad->GetListOfPrimitives()->FindObject(this)==this)
230 {
231 fDrawingPad->RecursiveRemove(this);
232 delete fDrawingPad;
233 }
234}
235
236// ------------------------------------------------------------------------
237//
238// This is called at any time the canvas should get repainted.
239// Here we maintain an aspect ratio of 5/4=1.15. This makes sure,
240// that the camera image doesn't get distorted by resizing the canvas.
241//
242void MagicSnake::Paint(Option_t *opt)
243{
244 const UInt_t w = (UInt_t)(gPad->GetWw()*gPad->GetAbsWNDC());
245 const UInt_t h = (UInt_t)(gPad->GetWh()*gPad->GetAbsHNDC());
246
247 //
248 // Check for a change in width or height, and make sure, that the
249 // first call also sets the range
250 //
251 if (w*fH == h*fW && fW && fH)
252 return;
253
254 //
255 // Calculate aspect ratio (5/4=1.25 recommended)
256 //
257 const Double_t ratio = (Double_t)w/h;
258
259 Float_t x;
260 Float_t y;
261
262 if (ratio>1.0)
263 {
264 x = fRange*(ratio*2-1);
265 y = fRange;
266 }
267 else
268 {
269 x = fRange;
270 y = fRange/ratio;
271 }
272
273 fH = h;
274 fW = w;
275
276 //
277 // Set new range
278 //
279 fDrawingPad->Range(-fRange, -y, x, y);
280}
281
282// ------------------------------------------------------------------------
283//
284// Call this function to draw the camera layout into your canvas.
285// Setup a drawing canvas. Add this object and all child objects
286// (hexagons, etc) to the current pad. If no pad exists a new one is
287// created.
288//
289void MagicSnake::Draw(Option_t *option)
290{
291 //
292 // if no canvas is yet existing to draw into, create a new one
293 //
294 /*TCanvas *c =*/ new TCanvas("MagicSnake", "Magic Snake", 0, 0, 800, 800);
295 //c->ToggleEventStatus();
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 // Reset the game pad
308 //
309 Reset();
310 DrawHexagons();
311
312 fShow.SetTextAlign(23); // centered/bottom
313#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
314 fShow.SetBit(kNoContextMenu|kCannotPick);
315#endif
316 fShow.Draw();
317}
318
319void MagicSnake::Update()
320{
321 TString txt = "Pixels: ";
322 txt += fNumPixels;
323 txt += " Bombs: ";
324 txt += fNumBombs;
325 txt += " Food: ";
326 txt += fNumFood;
327
328 fShow.SetText(0, fRange, txt);
329}
330
331// ------------------------------------------------------------------------
332//
333// reset the all pixel colors to a default value
334//
335void MagicSnake::Reset()
336{
337 fDirection = fGeomCam->InheritsFrom("MGeomCamCT1") ? kLeft : kRight;
338 fLength = 2;
339
340 for (UInt_t i=0; i<fNumPixels; i++)
341 {
342 (*this)[i].SetFillColor(kBackground);
343 (*fGeomCam)[i].ResetBit(kUserBits);
344 }
345
346 (*fGeomCam)[0].SetBit(kHasWorm);
347
348 fNumBombs = fNumPixels/30;
349
350 TRandom rnd(0);
351 for (int i=0; i<fNumBombs; i++)
352 {
353 Int_t idx;
354
355 do idx = (Int_t)rnd.Uniform(fNumPixels);
356 while ((*fGeomCam)[idx].TestBits(kHasBomb|kHasWorm));
357
358 (*fGeomCam)[idx].SetBit(kHasBomb);
359 (*this)[idx].SetFillColor(kRed);
360 }
361
362 fNumFood = fNumPixels/6;
363
364 fArray = new Int_t[fNumFood+3];
365
366 fArray[0] = 0;
367 fArray[1] = 1;
368
369 for (int i=0; i<fNumFood+3; i++)
370 {
371 Float_t f = (float)i/(fNumFood+3);
372 gROOT->GetColor(51+i)->SetRGB(f, f, f);
373 }
374 for (int i=0; i<fNumFood; i++)
375 {
376 Int_t idx;
377
378 do idx = (Int_t)rnd.Uniform(fNumPixels);
379 while ((*fGeomCam)[idx].TestBits(kHasBomb|kHasFood|kHasWorm));
380
381 (*fGeomCam)[idx].SetBit(kHasFood);
382 (*this)[idx].SetFillColor(kGreen);
383 }
384
385 SetWormColor();
386
387 for (int i=0; i<2; i++)
388 {
389 Int_t idx;
390
391 do idx = (Int_t)rnd.Uniform(fNumPixels);
392 while ((*fGeomCam)[idx].TestBits(kHasBomb|kHasFood|kHasWorm|kHasTransport));
393
394 fTransport[i] = idx;
395 (*fGeomCam)[idx].SetBit(kHasTransport);
396 (*this)[idx].SetFillColor(kYellow);
397 }
398
399 fDrawingPad->SetFillColor(22);
400
401#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
402 fDrawingPad->SetBit(kNoContextMenu);
403 SetBit(kNoContextMenu);
404#endif
405
406 if (!fDone)
407 return;
408
409 delete fDone;
410 fDone = NULL;
411}
412
413void MagicSnake::Done(TString txt, Int_t col)
414{
415 //(*this)[fArray[fLength-1]].SetFillColor(kBlue);
416
417 fDone = new TText(0, 0, txt);
418 fDone->SetTextColor(kWhite); // white
419 fDone->SetTextAlign(22); // centered/centered
420 fDone->SetTextSize(0.05); // white
421 fDone->Draw();
422 fDrawingPad->SetFillColor(col);
423#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
424 fDone->SetBit(kNoContextMenu|kCannotPick);
425 fDrawingPad->ResetBit(kNoContextMenu);
426 ResetBit(kNoContextMenu);
427#endif
428}
429
430void MagicSnake::Remove(TObject *obj)
431{
432 fDrawingPad->RecursiveRemove(obj);
433}
434
435// ------------------------------------------------------------------------
436//
437// Execute a mouse event on the camera
438//
439void MagicSnake::ExecuteEvent(Int_t event, Int_t keycode, Int_t keysym)
440{
441 if (event==kMouseEnter || event==kMouseMotion)
442 {
443 Pause(kFALSE);
444 return;
445 }
446
447 if (fDone)
448 return;
449
450 if (event==kMouseLeave)
451 {
452 Pause();
453 return;
454 }
455 if (event!=kKeyPress)
456 return;
457
458 switch (keysym)
459 {
460 case kKey_Left:
461 fDirection --;
462 break;
463
464 case kKey_Right:
465 fDirection ++;
466 break;
467
468 case kKey_Escape:
469 Done("Reset...", 22);
470 Reset();
471 return;
472
473 default:
474 cout << "Keysym=0x" << hex << keysym << endl;
475 }
476
477 if (fDirection < kRightTop)
478 fDirection = kLeftTop;
479 if (fDirection > kLeftTop)
480 fDirection = kRightTop;
481}
482
483void MagicSnake::Step(Int_t newpix)
484{
485 if ((*fGeomCam)[newpix].TestBit(kHasTransport))
486 {
487 (*this)[fArray[0]].SetFillColor(kBackground);
488 (*fGeomCam)[fArray[0]].ResetBit(kHasWorm);
489
490 for (int i=1; i<fLength; i++)
491 fArray[i-1] = fArray[i];
492
493 fArray[fLength-1] = newpix==fTransport[0]?fTransport[1]:fTransport[0];
494
495 return;
496 }
497
498 if (!(*fGeomCam)[newpix].TestBit(kHasFood))
499 {
500 MGeomPix &pix = (*fGeomCam)[fArray[0]];
501
502 if (!pix.TestBit(kHasTransport))
503 if (pix.TestBit(kHasDoor))
504 (*this)[fArray[0]].SetFillColor(kMagenta);
505 else
506 (*this)[fArray[0]].SetFillColor(kBackground);
507
508 pix.ResetBit(kHasWorm);
509
510 for (int i=1; i<fLength; i++)
511 fArray[i-1] = fArray[i];
512
513 fArray[fLength-1] = newpix;
514 }
515 else
516 {
517 fArray[fLength++] = newpix;
518 (*fGeomCam)[newpix].ResetBit(kHasFood);
519
520 fNumFood--;
521
522 if (fNumFood==0)
523 for (int i=1; i<7; i++)
524 {
525 (*this)[i].SetFillColor(kMagenta);
526 (*fGeomCam)[i].SetBit(kHasDoor);
527 }
528 }
529
530 SetWormColor();
531}
532
533void MagicSnake::SetWormColor()
534{
535 for (int i=0; i<fLength; i++)
536 {
537 const Int_t idx = fArray[i];
538
539 MGeomPix &pix = (*fGeomCam)[idx];
540
541 if (pix.TestBit(kHasTransport))
542 continue;
543
544 pix.SetBit(kHasWorm);
545
546 Int_t color = 51+fLength-i;
547 (*this)[idx].SetFillColor(color);
548 }
549}
550
551Int_t MagicSnake::ScanNeighbours()
552{
553 const Int_t first = fArray[fLength-1];
554
555 const MGeomPix &pix=(*fGeomCam)[first];
556
557 Double_t dx = pix.GetX();
558 Double_t dy = pix.GetY();
559
560 Int_t newpix = -1;
561 for (int i=0; i<pix.GetNumNeighbors(); i++)
562 {
563 const Int_t idx = pix.GetNeighbor(i);
564 const MGeomPix &next = (*fGeomCam)[idx];
565
566 const Double_t x = next.GetX();
567 const Double_t y = next.GetY();
568
569 switch (fDirection)
570 {
571 case kRightTop: if (x>=dx && y>dy) { newpix=idx; dy=y; } continue;
572 case kRight: if (x>dx) { newpix=idx; dx=x; } continue;
573 case kRightBottom: if (x>=dx && y<dy) { newpix=idx; dy=y; } continue;
574 case kLeftTop: if (x<=dx && y>dy) { newpix=idx; dy=y; } continue;
575 case kLeft: if (x<dx) { newpix=idx; dx=x; } continue;
576 case kLeftBottom: if (x<=dx && y<dy) { newpix=idx; dy=y; } continue;
577 }
578 }
579
580 if (newpix<0)
581 return -1;
582
583 const MGeomPix &np = (*fGeomCam)[newpix];
584
585 if (fNumFood==0 && np.TestBit(kHasDoor))
586 return -4;
587
588 if (np.TestBit(kHasBomb))
589 return -2;
590
591 if (np.TestBit(kHasWorm))
592 return -3;
593
594 return newpix;
595}
596
597Bool_t MagicSnake::HandleTimer(TTimer *timer)
598{
599 if (fDone || fPaused)
600 return kTRUE;
601
602 const Int_t newpix = ScanNeighbours();
603
604 switch (newpix)
605 {
606 case -1:
607 Done("You crashed! Don't drink and drive!", kRed);
608 break;
609 case -2:
610 Done("Ouch, you found the bomb!", kRed);
611 break;
612 case -3:
613 Done("Argh... don't eat yourself!", kRed);
614 break;
615 case -4:
616 Done("Congratulations! You won the game!", kGreen);
617 break;
618 default:
619 Step(newpix);
620 }
621
622 Update();
623
624 //cout << "Update " << flush;
625
626 fDrawingPad->Modified();
627 fDrawingPad->Update();
628
629 //cout << "Done." << endl;
630
631 return kTRUE;
632}
Note: See TracBrowser for help on using the repository browser.