    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: