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: