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.
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.