001: #include <wx/wx.h> 002: #include <string> 003: #include <cstdlib> 004: 005: const double PI = 3.141592653589793; 006: 007: const int ID_GUESS = 1000; 008: const int ID_PLAYER_NAME = 1001; 009: const int ID_PLAYER_LEVEL = 1002; 010: 011: /** 012: A clock that can draw its face. 013: */ 014: class Clock 015: { 016: public: 017: /** 018: Constructs a clock with a given center and radius. 019: @param x the x-coordinate of the center 020: @param y the y-coordinate of the center 021: @param r the radius of the clock 022: */ 023: Clock(int x, int y, int r); 024: 025: /** 026: Sets the current time. 027: @param h the hours to set 028: @param m the minutes to set 029: */ 030: void set_time(int h, int m); 031: 032: /** 033: Draws the clock face, with tick marks and hands. 034: @param dc the device context to draw on 035: */ 036: void draw(wxDC& dc) const; 037: private: 038: /** 039: Draw a tick mark (hour or minute mark). 040: @param dc the device context to draw on 041: @param angle the angle in minutes (0...59, 0 = top) 042: @param length the length of the tick mark, as a fraction 043: of the radius (between 0.0 and 1.0) 044: */ 045: void draw_tick(wxDC& dc, double angle, double length) const; 046: 047: /** 048: Draw a hand, starting from the center. 049: @param dc the device context to draw on 050: @param angle the angle in minutes (0...59, 0 = top) 051: @param length the length of the hand, as a fraction 052: of the radius (between 0.0 and 1.0) 053: */ 054: void draw_hand(wxDC& dc, double angle, double length) const; 055: 056: int hours; 057: int minutes; 058: int centerx; 059: int centery; 060: int radius; 061: }; 062: 063: /** 064: The player of the clock game. 065: */ 066: class Player 067: { 068: public: 069: /** 070: Constructs a player with name "Player", 071: level 1, and score 0. 072: */ 073: Player(); 074: 075: /** 076: Increments the score. Moves to next level if current 077: level complete 078: */ 079: void increment_score(); 080: 081: /** 082: Gets the current level. 083: @return the level 084: */ 085: int get_level() const; 086: 087: /** 088: Gets the player's name. 089: @return the name 090: */ 091: string get_name() const; 092: 093: /** 094: Sets the player's level. 095: @param l the level 096: */ 097: void set_level(int l); 098: 099: /** 100: Sets the player's name. 101: @param n the name 102: */ 103: void set_name(string n); 104: private: 105: string name; 106: int score; 107: int level; 108: }; 109: 110: /** 111: The window that shows the clock. 112: */ 113: class ClockWindow : public wxWindow 114: { 115: public: 116: /** 117: Constructs a clock window. 118: @param parent the parent window 119: */ 120: ClockWindow(wxWindow* parent); 121: /** 122: Sets the time of the clock and repaints it. 123: @param h the hours 124: @param m the minutes 125: */ 126: void set_time(int h, int m); 127: /** 128: Paints the clock. 129: @param event the event descriptor 130: */ 131: void OnPaint(wxPaintEvent& event); 132: private: 133: Clock clock; 134: DECLARE_EVENT_TABLE() 135: }; 136: 137: /** 138: The frame that contains the clock window and the 139: fields for entering a guess. 140: */ 141: class GameFrame : public wxFrame 142: { 143: public: 144: /** 145: Constructs the game frame. 146: */ 147: GameFrame(); 148: 149: /** 150: Starts a new round, with a new clock time. 151: */ 152: void new_round(); 153: /** 154: Processes the player's guess. 155: @param event the event descriptor 156: */ 157: void OnGuess(wxCommandEvent& event); 158: /** 159: Prompts the player to enter a name. 160: @param event the event descriptor 161: */ 162: void OnPlayerName(wxCommandEvent& event); 163: /** 164: Prompts the player to enter a level. 165: @param event the event descriptor 166: */ 167: void OnPlayerLevel(wxCommandEvent& event); 168: private: 169: ClockWindow* window; 170: wxTextCtrl* hour_text; 171: wxTextCtrl* minute_text; 172: Player player; 173: int current_hours; 174: int current_minutes; 175: int tries; 176: DECLARE_EVENT_TABLE() 177: }; 178: 179: /** 180: The clock game application. 181: */ 182: class GameApp : public wxApp 183: { 184: public: 185: /** 186: Constructs the application. 187: */ 188: GameApp(); 189: virtual bool OnInit(); 190: private: 191: GameFrame* frame; 192: }; 193: 194: DECLARE_APP(GameApp) 195: 196: IMPLEMENT_APP(GameApp) 197: 198: BEGIN_EVENT_TABLE(ClockWindow, wxWindow) 199: EVT_PAINT(ClockWindow::OnPaint) 200: END_EVENT_TABLE() 201: 202: BEGIN_EVENT_TABLE(GameFrame, wxFrame) 203: EVT_BUTTON(ID_GUESS, GameFrame::OnGuess) 204: EVT_MENU(ID_PLAYER_NAME, GameFrame::OnPlayerName) 205: EVT_MENU(ID_PLAYER_LEVEL, GameFrame::OnPlayerLevel) 206: END_EVENT_TABLE() 207: 208: /** 209: Sets the seed of the random number generator. 210: */ 211: void rand_seed() 212: { 213: int seed = static_cast<int>(time(0)); 214: srand(seed); 215: } 216: 217: /** 218: Returns a random integer in a range. 219: @param a the bottom of the range 220: @param b the top of the range 221: @return a random number x, a <= x and x <= b 222: */ 223: int rand_int(int a, int b) 224: { 225: return a + rand() % (b - a + 1); 226: } 227: 228: Clock::Clock(int x, int y, int r) 229: { 230: centerx = x; 231: centery = y; 232: radius = r; 233: } 234: 235: void Clock::set_time(int h, int m) 236: { 237: hours = h; 238: minutes = m; 239: } 240: 241: void Clock::draw_tick(wxDC& dc, double angle, 242: double length) const 243: { 244: double alpha = -PI / 2 + 6 * angle * PI / 180; 245: dc.DrawLine( 246: centerx + static_cast<int>( 247: cos(alpha) * radius * (1 - length)), 248: centery + static_cast<int>( 249: sin(alpha) * radius * (1 - length)), 250: centerx + static_cast<int>(cos(alpha) * radius), 251: centery + static_cast<int>(sin(alpha) * radius)); 252: } 253: 254: void Clock::draw_hand(wxDC& dc, double angle, 255: double length) const 256: { 257: double alpha = -PI / 2 + 6 * angle * PI / 180; 258: dc.DrawLine(centerx, centery, 259: centerx + static_cast<int>(cos(alpha) * radius * length), 260: centery + static_cast<int>(sin(alpha) * radius * length)); 261: } 262: 263: void Clock::draw(wxDC& dc) const 264: { 265: dc.DrawEllipse(centerx - radius, centery - radius, 266: 2 * radius, 2 * radius); 267: const double HOUR_TICK_LENGTH = 0.2; 268: const double MINUTE_TICK_LENGTH = 0.1; 269: const double HOUR_HAND_LENGTH = 0.6; 270: const double MINUTE_HAND_LENGTH = 0.75; 271: for (int i = 0; i < 12; i++) 272: { 273: draw_tick(dc, i * 5, HOUR_TICK_LENGTH); 274: int j; 275: for (j = 1; j <= 4; j++) 276: draw_tick(dc, i * 5 + j, MINUTE_TICK_LENGTH); 277: } 278: draw_hand(dc, minutes, MINUTE_HAND_LENGTH); 279: draw_hand(dc, (hours + minutes / 60.0) * 5, HOUR_HAND_LENGTH); 280: } 281: 282: Player::Player() 283: { 284: name = "Player"; 285: level = 1; 286: score = 0; 287: } 288: 289: void Player::set_level(int l) 290: { 291: level = l; 292: } 293: 294: void Player::set_name(string n) 295: { 296: name = n; 297: } 298: 299: int Player::get_level() const 300: { 301: return level; 302: } 303: 304: string Player::get_name() const 305: { 306: return name; 307: } 308: 309: void Player::increment_score() 310: { 311: score++; 312: if (score % 5 == 0 && level < 4) 313: level++; 314: } 315: 316: ClockWindow::ClockWindow(wxWindow* parent) 317: : wxWindow(parent, -1), 318: clock(200, 200, 200) 319: { 320: } 321: 322: void ClockWindow::OnPaint(wxPaintEvent& event) 323: { 324: wxPaintDC dc(this); 325: dc.SetBrush(*wxTRANSPARENT_BRUSH); 326: clock.draw(dc); 327: } 328: 329: void ClockWindow::set_time(int h, int m) 330: { 331: clock.set_time(h, m); 332: Refresh(); 333: } 334: 335: GameFrame::GameFrame() 336: : wxFrame(NULL, -1, "GameFrame") 337: { 338: // initialize menu 339: wxMenu* menu = new wxMenu(); 340: menu->Append(ID_PLAYER_NAME, "Name"); 341: menu->Append(ID_PLAYER_LEVEL, "Level"); 342: 343: // add menu to menu bar 344: wxMenuBar* menu_bar = new wxMenuBar(); 345: SetMenuBar(menu_bar); 346: menu_bar->Append(menu, "Player"); 347: 348: window = new ClockWindow(this); 349: 350: hour_text = new wxTextCtrl(this, -1); 351: minute_text = new wxTextCtrl(this, -1); 352: 353: wxButton* guess_button = new wxButton(this, 354: ID_GUESS, "Guess"); 355: 356: wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL); 357: bottom_sizer->Add(new wxStaticText(this, -1, "Hours")); 358: bottom_sizer->Add(hour_text); 359: bottom_sizer->Add(new wxStaticText(this, -1, "Minutes")); 360: bottom_sizer->Add(minute_text); 361: bottom_sizer->Add(guess_button); 362: 363: wxBoxSizer* frame_sizer = new wxBoxSizer(wxVERTICAL); 364: frame_sizer->Add(window, 1, wxGROW); 365: frame_sizer->Add(bottom_sizer, 0, wxALIGN_CENTER); 366: 367: SetAutoLayout(true); 368: SetSizer(frame_sizer); 369: 370: new_round(); 371: } 372: 373: void GameFrame::OnGuess(wxCommandEvent& event) 374: { 375: tries++; 376: int hours = atoi(hour_text->GetValue().c_str()); 377: int minutes = atoi(minute_text->GetValue().c_str()); 378: if (hours < 1 || hours > 12) 379: { 380: wxMessageDialog* dialog = new wxMessageDialog(this, 381: "Hours must be between 1 and 12"); 382: dialog->ShowModal(); 383: dialog->Destroy(); 384: return; 385: } 386: if (minutes < 0 || minutes > 59) 387: { 388: wxMessageDialog* dialog = new wxMessageDialog(this, 389: "Hours must be between 1 and 12"); 390: dialog->ShowModal(); 391: dialog->Destroy(); 392: return; 393: } 394: if (current_hours == hours && current_minutes == minutes) 395: { 396: string text = "Congratulations, " + player.get_name() 397: + "! That is correct."; 398: wxMessageDialog* dialog = new wxMessageDialog(this, 399: text.c_str()); 400: dialog->ShowModal(); 401: dialog->Destroy(); 402: player.increment_score(); 403: new_round(); 404: } 405: else 406: { 407: string text = "Sorry, " + player.get_name() 408: + "! That is not correct."; 409: wxMessageDialog* dialog = new wxMessageDialog(this, 410: text.c_str()); 411: dialog->ShowModal(); 412: dialog->Destroy(); 413: if (tries == 2) new_round(); 414: } 415: } 416: 417: void GameFrame::new_round() 418: { 419: tries = 0; 420: int level = player.get_level(); 421: if (level == 1) current_minutes = 0; 422: else if (level == 2) current_minutes = 15 * rand_int(0, 3); 423: else if (level == 3) current_minutes = 5 * rand_int(0, 11); 424: else current_minutes = rand_int(0, 59); 425: current_hours = rand_int(1, 12); 426: window->set_time(current_hours, current_minutes); 427: } 428: 429: void GameFrame::OnPlayerName(wxCommandEvent& event) 430: { 431: wxTextEntryDialog* dialog = new wxTextEntryDialog(this, 432: "What is your name?"); 433: dialog->ShowModal(); 434: player.set_name(dialog->GetValue().c_str()); 435: dialog->Destroy(); 436: } 437: 438: void GameFrame::OnPlayerLevel(wxCommandEvent& event) 439: { 440: wxTextEntryDialog* dialog = new wxTextEntryDialog(this, 441: "At what level do you want to play? (1-4)"); 442: dialog->ShowModal(); 443: int level = atoi(dialog->GetValue().c_str()); 444: dialog->Destroy(); 445: if (level < 1 || level > 4) 446: { 447: wxMessageDialog* dialog = new wxMessageDialog(this, 448: "The level must be between 1 and 4"); 449: dialog->ShowModal(); 450: dialog->Destroy(); 451: return; 452: } 453: player.set_level(level); 454: } 455: 456: GameApp::GameApp() 457: { 458: rand_seed(); 459: frame = new GameFrame(); 460: } 461: 462: bool GameApp::OnInit() 463: { 464: frame->Show(true); 465: return true; 466: } 467: