#StackBounty: #c++ #game #gui #c++17 Hunt the Wumpus GUI (FLTK)

Bounty: 50

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:

wumpus_window.h

    #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 {
    public:
        Wumpus_window();
    private:
        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 {
        private:
            static constexpr auto count_of_neighbours = 3;
        public:
            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"; }
        private:
            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);
}

#endif

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.


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.