I used the code from the text based hunt the wumpus game discussed here: Text based game “Hunt the Wumpus” Version 3 to create a gui Version based on excercises from PPP by Stroustrup.

enter image description here enter image description here

For the GUI i used FLTK because the book is based on it. I used it with MSVS2017. I got FLTK to run with this tutorial: https://bumpyroadtocode.com/2017/08/05/how-to-install-and-use-fltk-1-3-4-in-visual-studio-2017-complete-guide/

Also Stroustrup provides some basic GUI library which wraps the FLTK functions: http://www.stroustrup.com/Programming/PPP2code/

I modified the headers sligtly by removing the std_lib_facilities.h because it includes many unessary stuff and basically hides which c++ standard headers are included.

Unfortunately the source code is a bit to big to get posted here, espcially the wumpus_window.cpp file. I do some repetive stuff there which i think is the reason i have to much code.

Here is the class declaration for the Window and the Rooms:


    #ifndef WUMPUS_WINDOW_GUARD_060820182207
#define WUMPUS_WINDOW_GUARD_060820182207

#include "Window.h"
#include "GUI.h"
#include "Point.h"

#include <array>

namespace wumpus {

    using Room_number = int;
    using Arrows = int;

    class Wumpus_window : public Graph_lib::Window {
        static const Point window_offset_xy;

        static constexpr auto window_size_x = 1600;
        static constexpr auto window_size_y = 1000;

        static constexpr auto count_of_pits = 2;
        static constexpr auto count_of_bats = 2;
        static constexpr auto count_of_rooms = 20;

        static constexpr auto count_of_arrows = 5;

        Arrows arrows = count_of_arrows;
        int wins = 0;
        int losts = 0;

        class Room : public Graph_lib::Rectangle {
            static constexpr auto count_of_neighbours = 3;
            Room(Room* first, Room* second, Room* third, Point xy, int len);

            std::array <Room*, count_of_neighbours> get_neigbours() const { return m_neighbours; }

            void set_wumpus()                       { m_has_wumpus = true; }
            void reset_wumpus()                     { m_has_wumpus = false; }
            bool has_wumpus()               const   { return m_has_wumpus; }

            void set_bat()                          { m_has_bat = true; }
            void reset_bat()                        { m_has_bat = false; }
            bool has_bat()                  const   { return m_has_bat; }

            void set_pit()                          { m_has_pit = true; }
            void reset_pit()                        { m_has_pit = false; }
            bool has_pit()                  const   { return m_has_pit; }

            void set_player();
            void reset_player();
            bool has_player()               const   { return m_has_player; }

            void reset_player_was_here()            { m_player_was_here = false; }
            bool player_was_here()          const   { return m_player_was_here; }

            void set_guess_wumpus();
            void reset_guess_wumpus();
            bool guess_wumpus()             const { return m_guess_wumpus; }

            void set_guess_bat();
            void reset_guess_bat();
            bool guess_bat()                const { return m_guess_bat; }

            void set_guess_pit();
            void reset_guess_pit();
            bool guess_pit()                const { return m_guess_pit; }

            Room_number get_room_number()   const { return m_room_number; }
            void set_room_number(Room_number room_number) { m_room_number = room_number; }

            void connect_to_neigbour_rooms_with_lines();

            void hide_all();

            Point btn_move_xy()                 const   { return Point{ point(0).x,point(0).y + height() }; }
            int btn_move_size_x()               const   { return width(); }
            int btn_move_size_y()               const   { return height() / 3; }
            const char* btn_move_label()        const   { return "move"; }

            Point btn_guess_wumpus_xy()         const   { return Point{ point(0).x + width(), point(0).y }; }
            int btn_guess_wumpus_size_x()       const   { return width() / 3; }
            int btn_guess_wumpus_size_y()       const   { return height() / 3; }
            const char* btn_guess_wumpus_label()const   { return "w"; }

            Point btn_guess_bat_xy()            const   { return Point{ point(0).x + width(), point(0).y + height() /3 }; }
            int btn_guess_bat_size_x()          const   { return width() / 3; }
            int btn_guess_bat_size_y()          const   { return height() / 3; }
            const char* btn_guess_bat_label()   const   { return "b"; }

            Point btn_guess_pit_xy()            const   { return Point{ point(0).x + width(), point(0).y + (height() /3) * 2 }; }
            int btn_guess_pit_size_x()          const   { return width() / 3; }
            int btn_guess_pit_size_y()          const   { return height() / 3; }
            const char* btn_guess_pit_label()   const   { return "p"; }
            std::array <Room*, count_of_neighbours> m_neighbours;

            bool m_has_wumpus{ false };
            bool m_has_bat{ false };
            bool m_has_pit{ false };
            bool m_has_player{ false };
            bool m_player_was_here{ false };

            bool m_guess_wumpus{ false };
            bool m_guess_bat{ false };
            bool m_guess_pit{ false };

            Room_number m_room_number{ 0 };

            static constexpr auto room_color                            = Graph_lib::Color::black;
            static constexpr auto room_fill_color_neigbour_room         = Graph_lib::Color::yellow;
            static constexpr auto room_fill_color_player_room           = Graph_lib::Color::blue;
            static constexpr auto room_fill_color_player_was_here_room  = Graph_lib::Color::green;

            Point m_center_xy;

            // Dynamically is necessary here because connections can only be drawn after the rooms are connected 
            // with their neigbours
            Graph_lib::Vector_ref <Graph_lib::Line> line_connections;

            void draw_lines() const override;

            static constexpr auto rectangle_guess_wumpus_color = Graph_lib::Color::black;
            static constexpr auto rectangle_guess_wumpus_fill_color = Graph_lib::Color::dark_red;
            Graph_lib::Rectangle rectangle_guess_wumpus;

            static constexpr auto rectangle_guess_bat_color = Graph_lib::Color::black;
            static constexpr auto rectangle_guess_bat_fill_color = Graph_lib::Color::dark_cyan;
            Graph_lib::Rectangle rectangle_guess_bat;

            static constexpr auto rectangle_guess_pit_color = Graph_lib::Color::black;
            static constexpr auto rectangle_guess_pit_fill_color = Graph_lib::Color::dark_yellow;
            Graph_lib::Rectangle rectangle_guess_pit;

            Graph_lib::Text txt_room_number;

        static constexpr auto room_size_xy = 70;
        static const std::array<Point, 20> room_xy;

        std::array<Room, count_of_rooms> rooms
                { &rooms[1]     ,&rooms[4],     &rooms[19], room_xy[0], room_size_xy },
                { &rooms[0]     ,&rooms[2],     &rooms[17], room_xy[1], room_size_xy },
                { &rooms[1]     ,&rooms[3],     &rooms[15], room_xy[2], room_size_xy }, 
                { &rooms[2]     ,&rooms[4],     &rooms[13], room_xy[3], room_size_xy },
                { &rooms[0]     ,&rooms[3],     &rooms[5],  room_xy[4], room_size_xy },
                { &rooms[4]     ,&rooms[6],     &rooms[12], room_xy[5], room_size_xy },
                { &rooms[5]     ,&rooms[7],     &rooms[19], room_xy[6], room_size_xy },
                { &rooms[6]     ,&rooms[8],     &rooms[11], room_xy[7], room_size_xy },
                { &rooms[7]     ,&rooms[9],     &rooms[18], room_xy[8], room_size_xy }, 
                { &rooms[8]     ,&rooms[10],    &rooms[16], room_xy[9], room_size_xy },
                { &rooms[9]     ,&rooms[11],    &rooms[14], room_xy[10],room_size_xy },
                { &rooms[7]     ,&rooms[10],    &rooms[12], room_xy[11],room_size_xy },
                { &rooms[5]     ,&rooms[11],    &rooms[13], room_xy[12],room_size_xy },
                { &rooms[3]     ,&rooms[12],    &rooms[14], room_xy[13],room_size_xy },
                { &rooms[10]    ,&rooms[13],    &rooms[15], room_xy[14],room_size_xy },
                { &rooms[2]     ,&rooms[14],    &rooms[16], room_xy[15],room_size_xy },
                { &rooms[9]     ,&rooms[15],    &rooms[17], room_xy[16],room_size_xy },
                { &rooms[1]     ,&rooms[16],    &rooms[18], room_xy[17],room_size_xy },
                { &rooms[8]     ,&rooms[17],    &rooms[19], room_xy[18],room_size_xy },
                { &rooms[0]     ,&rooms[6],     &rooms[18], room_xy[19],room_size_xy },

        static const std::vector<std::string>   txt_instructions_txt;
        static const Point                      txt_instructions_xy;
        static constexpr auto                   txt_instructions_offset_y = 28;
        static constexpr auto                   txt_instructions_font_size = 20;
        static constexpr auto                   txt_instructions_color  = Graph_lib::Color::black;
        Graph_lib::Vector_ref <Graph_lib::Text> txt_instructions;
        void print_game_instructions();

        static const Point      btn_start_again_xy;
        static constexpr auto   btn_start_again_size_x = 150;
        static constexpr auto   btn_start_again_size_y = 75;
        static constexpr auto   btn_start_again_label = "Start";
        Graph_lib::Button       btn_start;
        void btn_start_event();
        void init_window();
        void init_dungeon();

        static const Point      txt_indicate_wumpus_is_near_xy;
        static constexpr auto   txt_indicate_wumpus_is_near_font_size = 60;
        static constexpr auto   txt_indicate_wumpus_is_near_txt = "I smell the wumpus";
        static constexpr auto   txt_indicate_wumpus_is_near_color = Graph_lib::Color::dark_red;
        Graph_lib::Text         txt_indicate_wumpus_is_near;

        static const Point      txt_indicate_bat_is_near_xy;
        static constexpr auto   txt_indicate_bat_is_near_font_size = 60;
        static constexpr auto   txt_indicate_bat_is_near_txt = "I hear a bat";
        static constexpr auto   txt_indicate_bat_is_near_color = Graph_lib::Color::dark_cyan;
        Graph_lib::Text         txt_indicate_bat_is_near;

        static const Point      txt_indicate_pit_is_near_xy;
        static constexpr auto   txt_indicate_pit_is_near_font_size = 60;
        static constexpr auto   txt_indicate_pit_is_near_txt = "I feel a breeze";
        static constexpr auto   txt_indicate_pit_is_near_color = Graph_lib::Color::dark_yellow;
        Graph_lib::Text         txt_indicate_pit_is_near;

        static const Point      txt_indicate_dragged_by_bat_xy;
        static constexpr auto   txt_indicate_dragged_by_bat_font_size = 30;
        static constexpr auto   txt_indicate_dragged_by_bat_txt = "Gigantic bat appeared and dragged you to room ";
        static constexpr auto   txt_indicate_dragged_by_bat_color = Graph_lib::Color::red;
        Graph_lib::Text         txt_indicate_dragged_by_bat;

        void indicate_hazards();

        Graph_lib::Vector_ref <Graph_lib::Button> btn_move;
        void btn_move_0_event() { move_player(rooms[0]); }
        void btn_move_1_event() { move_player(rooms[1]); }
        void btn_move_2_event() { move_player(rooms[2]); }
        void btn_move_3_event() { move_player(rooms[3]); }
        void btn_move_4_event() { move_player(rooms[4]); }
        void btn_move_5_event() { move_player(rooms[5]); }
        void btn_move_6_event() { move_player(rooms[6]); }
        void btn_move_7_event() { move_player(rooms[7]); }
        void btn_move_8_event() { move_player(rooms[8]); }
        void btn_move_9_event() { move_player(rooms[9]); }
        void btn_move_10_event(){ move_player(rooms[10]); }
        void btn_move_11_event(){ move_player(rooms[11]); }
        void btn_move_12_event(){ move_player(rooms[12]); }
        void btn_move_13_event(){ move_player(rooms[13]); }
        void btn_move_14_event(){ move_player(rooms[14]); }
        void btn_move_15_event(){ move_player(rooms[15]); }
        void btn_move_16_event(){ move_player(rooms[16]); }
        void btn_move_17_event(){ move_player(rooms[17]); }
        void btn_move_18_event(){ move_player(rooms[18]); }
        void btn_move_19_event(){ move_player(rooms[19]); }

        void init_btns_move_to_room();
        void show_btns_move_to_room();

        void move_player(Room& room);

        Graph_lib::Vector_ref<Graph_lib::Button> btn_guess_wumpus;
        void btn_guess_wumpus_in_room_0_event() { guess_wumpus(rooms[0]); }
        void btn_guess_wumpus_in_room_1_event() { guess_wumpus(rooms[1]); }
        void btn_guess_wumpus_in_room_2_event() { guess_wumpus(rooms[2]); }
        void btn_guess_wumpus_in_room_3_event() { guess_wumpus(rooms[3]); }
        void btn_guess_wumpus_in_room_4_event() { guess_wumpus(rooms[4]); }
        void btn_guess_wumpus_in_room_5_event() { guess_wumpus(rooms[5]); }
        void btn_guess_wumpus_in_room_6_event() { guess_wumpus(rooms[6]); }
        void btn_guess_wumpus_in_room_7_event() { guess_wumpus(rooms[7]); }
        void btn_guess_wumpus_in_room_8_event() { guess_wumpus(rooms[8]); }
        void btn_guess_wumpus_in_room_9_event() { guess_wumpus(rooms[9]); }
        void btn_guess_wumpus_in_room_10_event(){ guess_wumpus(rooms[10]); }
        void btn_guess_wumpus_in_room_11_event(){ guess_wumpus(rooms[11]); }
        void btn_guess_wumpus_in_room_12_event(){ guess_wumpus(rooms[12]); }
        void btn_guess_wumpus_in_room_13_event(){ guess_wumpus(rooms[13]); }
        void btn_guess_wumpus_in_room_14_event(){ guess_wumpus(rooms[14]); }
        void btn_guess_wumpus_in_room_15_event(){ guess_wumpus(rooms[15]); }
        void btn_guess_wumpus_in_room_16_event(){ guess_wumpus(rooms[16]); }
        void btn_guess_wumpus_in_room_17_event(){ guess_wumpus(rooms[17]); }
        void btn_guess_wumpus_in_room_18_event(){ guess_wumpus(rooms[18]); }
        void btn_guess_wumpus_in_room_19_event(){ guess_wumpus(rooms[19]); }

        void init_btns_guess_wumpus();
        void show_btns_guess_wumpus_in_room();

        void guess_wumpus(Room& room);

        Graph_lib::Vector_ref<Graph_lib::Button> btn_guess_bat;
        void btn_guess_bat_in_room_0_event(){ guess_bat(rooms[0]); }
        void btn_guess_bat_in_room_1_event(){ guess_bat(rooms[1]); }
        void btn_guess_bat_in_room_2_event(){ guess_bat(rooms[2]); }
        void btn_guess_bat_in_room_3_event(){ guess_bat(rooms[3]); }
        void btn_guess_bat_in_room_4_event(){ guess_bat(rooms[4]); }
        void btn_guess_bat_in_room_5_event(){ guess_bat(rooms[5]); }
        void btn_guess_bat_in_room_6_event(){ guess_bat(rooms[6]); }
        void btn_guess_bat_in_room_7_event(){ guess_bat(rooms[7]); }
        void btn_guess_bat_in_room_8_event(){ guess_bat(rooms[8]); }
        void btn_guess_bat_in_room_9_event(){ guess_bat(rooms[9]); }
        void btn_guess_bat_in_room_10_event(){ guess_bat(rooms[10]); }
        void btn_guess_bat_in_room_11_event(){ guess_bat(rooms[11]); }
        void btn_guess_bat_in_room_12_event(){ guess_bat(rooms[12]); }
        void btn_guess_bat_in_room_13_event(){ guess_bat(rooms[13]); }
        void btn_guess_bat_in_room_14_event(){ guess_bat(rooms[14]); }
        void btn_guess_bat_in_room_15_event(){ guess_bat(rooms[15]); }
        void btn_guess_bat_in_room_16_event(){ guess_bat(rooms[16]); }
        void btn_guess_bat_in_room_17_event(){ guess_bat(rooms[17]); }
        void btn_guess_bat_in_room_18_event(){ guess_bat(rooms[18]); }
        void btn_guess_bat_in_room_19_event(){ guess_bat(rooms[19]); }

        void init_btns_guess_bat();
        void show_btns_guess_bat_in_room();

        void guess_bat(Room& room);

        Graph_lib::Vector_ref<Graph_lib::Button> btn_guess_pit;
        void btn_guess_pit_in_room_0_event(){ guess_pit(rooms[0]); }
        void btn_guess_pit_in_room_1_event(){ guess_pit(rooms[1]); }
        void btn_guess_pit_in_room_2_event(){ guess_pit(rooms[2]); }
        void btn_guess_pit_in_room_3_event(){ guess_pit(rooms[3]); }
        void btn_guess_pit_in_room_4_event(){ guess_pit(rooms[4]); }
        void btn_guess_pit_in_room_5_event(){ guess_pit(rooms[5]); }
        void btn_guess_pit_in_room_6_event(){ guess_pit(rooms[6]); }
        void btn_guess_pit_in_room_7_event(){ guess_pit(rooms[7]); }
        void btn_guess_pit_in_room_8_event(){ guess_pit(rooms[8]); }
        void btn_guess_pit_in_room_9_event(){ guess_pit(rooms[9]); }
        void btn_guess_pit_in_room_10_event(){ guess_pit(rooms[10]); }
        void btn_guess_pit_in_room_11_event(){ guess_pit(rooms[11]); }
        void btn_guess_pit_in_room_12_event(){ guess_pit(rooms[12]); }
        void btn_guess_pit_in_room_13_event(){ guess_pit(rooms[13]); }
        void btn_guess_pit_in_room_14_event(){ guess_pit(rooms[14]); }
        void btn_guess_pit_in_room_15_event(){ guess_pit(rooms[15]); }
        void btn_guess_pit_in_room_16_event(){ guess_pit(rooms[16]); }
        void btn_guess_pit_in_room_17_event(){ guess_pit(rooms[17]); }
        void btn_guess_pit_in_room_18_event(){ guess_pit(rooms[18]); }
        void btn_guess_pit_in_room_19_event(){ guess_pit(rooms[19]); }

        void init_btns_guess_pit();
        void show_btns_guess_pit_in_room();

        void guess_pit(Room& room);

        static const Point      out_remaining_arrows_xy;
        static constexpr auto   out_remaining_arrows_size_x = 60;
        static constexpr auto   out_remaining_arrows_size_y = 60;
        static constexpr auto   out_remaining_arrows_label = "Arrows";
        static constexpr auto   out_remaining_arrows_label_size = 60;
        static constexpr auto   out_remaining_arrows_out_size = 60;
        Graph_lib::Out_box      out_remaining_arrows;

        static const Point      in_arrow_target_rooms_xy;
        static constexpr auto   in_arrow_target_rooms_size_x = 250;
        static constexpr auto   in_arrow_target_rooms_size_y = 60;
        static constexpr auto   in_arrow_target_rooms_label = "Target";
        static constexpr auto   in_arrow_target_rooms_label_size = 60;
        static constexpr auto   in_arrow_target_rooms_in_size = 60;
        Graph_lib::In_box       in_arrow_target_rooms;

        static const Point      txt_invalid_input_xy;
        static constexpr auto   txt_invalid_input_font_size = 60;
        static constexpr auto   txt_invalid_input_txt = "Invalid Input";
        static constexpr auto   txt_invalid_input_color = Graph_lib::Color::black;
        Graph_lib::Text         txt_invalid_input;

        static const Point      btn_shoot_arrow_xy;
        static constexpr auto   btn_shoot_arrow_size_x = 100;
        static constexpr auto   btn_shoot_arrow_size_y = 60;
        static constexpr auto   btn_shoot_arrow_label = "Shoot!!!";
        Graph_lib::Button       btn_shoot_arrow;

        void btn_shoot_arrow_event();
        void shoot_arrow(const std::vector<Room_number>& target_rooms);
        void move_wumpus();

        static const Point      txt_game_over_1_row_xy;
        static const Point      txt_game_over_2_row_xy;
        static constexpr auto   txt_game_over_font_size = 100;
        static constexpr auto   txt_game_over_txt_win           = "YOU WON";
        static constexpr auto   txt_game_over_txt_win_wumpus    = "You killed Wumpus in room ";
        static constexpr auto   txt_game_over_txt_color_win     = Graph_lib::Color::dark_green;
        static constexpr auto   txt_game_over_txt_lose          = "YOU LOST";
        static constexpr auto   txt_game_over_txt_lose_wumpus       = "You got eaten by the Wumpus!";
        static constexpr auto   txt_game_over_txt_lose_wumpus_move= "The Wumpus enters your room!";
        static constexpr auto   txt_game_over_txt_lose_pit      = "You felt into a bottomless pit!";
        static constexpr auto   txt_game_over_txt_lose_arrow        = "You got killed by youre own arrow";
        static constexpr auto   txt_game_over_txt_lose_no_arrows    = "You ran out of arrows";
        static constexpr auto   txt_game_over_txt_color_lose        = Graph_lib::Color::red;
        Graph_lib::Text         txt_game_over_1_row;
        Graph_lib::Text         txt_game_over_2_row;

        void game_over(const std::string& txt_first_row, const std::string& txt_second_row, const Graph_lib::Color& txt_color);

        static const Point      btn_try_again_xy;
        static constexpr auto   btn_try_again_size_x = window_size_x / 3;
        static constexpr auto   btn_try_again_size_y = (window_size_y / 3);
        static constexpr auto   btn_try_again_label = "Try again";
        Graph_lib::Button       btn_try_again;
        void btn_try_again_event();

        static const Point      out_wins_xy;
        static constexpr auto   out_wins_size_x = 300;
        static constexpr auto   out_wins_size_y = 60;
        static constexpr auto   out_wins_label = "Won:";
        static constexpr auto   out_wins_label_size = 60;
        static constexpr auto   out_wins_out_size = 60;
        Graph_lib::Out_box      out_wins;

        static const Point      out_losts_xy;
        static constexpr auto   out_losts_size_x = 300;
        static constexpr auto   out_losts_size_y = 60;
        static constexpr auto   out_losts_label = "Lost:";
        static constexpr auto   out_losts_label_size = 60;
        static constexpr auto   out_losts_out_size = 60;
        Graph_lib::Out_box      out_losts;

        void show_state_of_dungeon();   // for debug only. shows in console which rooms are connected with which number

    std::vector<Room_number> get_target_rooms(const std::string& input);
    int hunt_the_wumpus();
    int get_random(int min, int max);


What bothers me here the most is the handling of the Buttons. I have 4 Buttons for each Room:

-guess room has a bat (draws a small Rectangle in the room indicate there could be a bat)

-guess room has the wumpus

-guess room has a pit

-move -> pushing trys to move the player into the room.

I tryed to include these Buttons directly into the class Room but i could only manage to put in the coordinates and the sizes.

If i create the Buttons inside the room and assign a callback function from inside the Room class the application crashes because i violate that Wumpus_window and not Room is responsible for draw the widgets (the small rectangles get displayed/ hided in the callbacks).

So currently i have these 80 numbered callback methods in Wumpus_window. Which would be alot less repetive in the Room class. With this initing the buttons is a lot of repetive work which cant be looped (e.g. void init_btns_guess_pit())

The complete source code can be found here: https://gist.github.com/sandro4912/e603a6ea5d425b299701565098be11fc

Feel free to comment anything else you find suspicious to Wumpus_window.h or Wumpus_window.cpp. The other files are the support files from Stroustrup slightly modified.



Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.