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: