Trying to follow a tutorial written in Go, segmentation faulting

I'm trying to follow a tutorial on how to create a roguelike in the Go language,and with BearLibTerminal. I'm trying to translate it into C++, though. And since I'm new to C++ (and programming in general) I've been stumbling quite a bit a long the way. My latest trip-up is with map generation. Here's the relevant code...

main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#includes...

const int WindowSizeX{20};
const int WindowSizeY{10};
const int MapWidth{WindowSizeX};
const int MapHeight{WindowSizeY};

more global variables...

Entity player;
Entity enemy;
Map map;

void render_map();
void render_entities();
void clear_entities();
void render_all();

int main() {

    initialize();

    while (!quit_game) {

        terminal_refresh();

        int key{terminal_read()};

        if (key != TK_CLOSE) {

            clear_entities();
            handle_input(key, player);
            render_all();
        }

        else {

            quit_game = true;
        }
    }

    terminal_close();

    return 0;
}

void initialize() {

    ....

    map.Width = MapWidth;
    map.Height = MapHeight;
    map.InitializeMap();

    ...
    
    render_all();
}

void handle_input(int key, Entity &entity) {

    Handle input....

    if (!map.CheckBlockMovement(player.X + dx, player.Y + dy)) {

        player.Move(dx, dy);
    }
}

void render_map() {

    for (int x = 0; x < map.Width; x++) {

        for (int y = 0; y < map.Height; y++) {

            if (map.CheckBlockMovement(x, y)) {

                terminal_color(color_from_name("gray"));
                terminal_print(x, y, "#");
            }
            else {

                terminal_color(color_from_name("brown"));
                terminal_print(x, y, ".");
            }
        }
    }
}

void render_all() {

    render_map();
    render_entities();
}


map.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef MAP_H_
#define MAP_H_

struct Tile {

    bool BlockMovement;
    bool BlockSight;
};

struct Map {

    int Width;
    int Height;
    Tile **Tiles;

    void InitializeMap();
    bool CheckBlockMovement(int x, int y);
    bool CheckBlockSight(int x, int y);
};

#endif /* MAP_H_ */ 


map.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <BLT/BearLibTerminal.h>
#include <iostream>
#include "map.h"

void Map::InitializeMap() {

    Tile *Tiles = new Tile[this->Width * this->Height];

    for (int x = 0; x < this->Width; x++) {

        std::cout << std::endl;

        for (int y = 0; y < this->Height; y++) {

            if (x == 0 || x == this->Width - 1 ||
                y == 0 || y == this->Height - 1) {

                // How do I do like the Go tutorial:
                // m.Tiles[x][y] = &Tile{true, true}

                std::cout << "#";
            }

            else {

                // m.Tiles[x][y] = &Tile{false, false}

                std::cout << ".";
            }
        }
    }

    std::cout << std::endl << std::endl;
}

bool Map::CheckBlockMovement(int x, int y) {

    // Go tutorial...
    // if m.Tiles[x][y].Blocked {
    //     return true
    // } else {
    //    return false
    // }

    // My failed attempt...
    if (this->Tiles[x + y]->BlockMovement) {

        return true;
    }
    else {

        return false;
    }
}


I'm getting a segmentation fault. Here's the ouput from valgrind.
1
2
3
4
5
6
7
==29715== Process terminating with default action of signal 11 (SIGSEGV)
==29715==  Access not within mapped region at address 0x0
==29715==    at 0x40569F: Map::CheckBlockMovement(int, int) (map.cpp:46)
==29715==    by 0x404AAA: render_map() (main.cpp:187)
==29715==    by 0x404A58: render_all() (main.cpp:217)
==29715==    by 0x4045B8: initialize() (main.cpp:93)
==29715==    by 0x4041D3: main (main.cpp:31)


All my attempts to fix this are for naught. I'm not sure exactly how to properly make and use something like Map::CheckBlockMovement. I've tried googling for things like "using a bool member function in an array" but I'm not getting anywhere. Any insight would be much appreciated.

The tutorial I'm following is here: http://jeremyceri.se/roguelikes/
1
2
3
4
void Map::InitializeMap() {
    Tile *Tiles = new Tile[this->Width * this->Height]; //`Tiles' is a local variable, which is different than this->Tiles
    //...
} //local variable dies 
http://www.cplusplus.com/doc/tutorial/namespaces/

I suppose that you set `Width' and `Height' somewhere, but that should be in the constructor
You're not allocating any memory for Map::Tiles since you are using a local variable of the same name instead.
BTW, your loops seem backwards to me. You might want to move the x loop inside and the y loop outside for printing (i.e., print row-by-row instead of column-by-column).
I haven't done that below, but you might want to.
And you aren't accessing your array correctly. You are using a 1D array to emulate a 2D array. You can't just add x and y to get the correct offset; you need to multiply y by Width (to get to the beginning of the correct row) then add x.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
struct Tile {
    bool BlockMovement;
    bool BlockSight;

    // Constructor
    Tile(bool m, bool s) : BlockMovement(m), BlockSight(s) {}
};

void Map::InitializeMap() {

    // Emulating a 2D array with a 1D array.
    Tiles = new Tile[Width * Height];

    for (int x = 0; x < Width; x++) {

        std::cout << std::endl;

        for (int y = 0; y < Height; y++) {

            if (x == 0 || x == Width - 1 ||
                y == 0 || y == Height - 1) {

                // How do I do like the Go tutorial:
                // m.Tiles[x][y] = &Tile{true, true}

                // We need to multiply y by Width to get to the beginning of row y,
                // then add x to get to the correct column
                Tiles[x + y * Width] = new Tile(true, true);

                std::cout << "#";
            }

            else {

                // m.Tiles[x][y] = &Tile{false, false}

                Tiles[x + y * Width] = new Tile(false, false);

                std::cout << ".";
            }
        }
    }

    std::cout << std::endl << std::endl;
}

bool Map::CheckBlockMovement(int x, int y) {

    // Go tutorial...
    // if m.Tiles[x][y].Blocked {
    //     return true
    // } else {
    //    return false
    // }

    return Tiles[x + y * Width]->BlockMovement;
}

Last edited on
Thank you very much. My attempts at fixing this were way off base, I have much to learn.

I'm getting a different error now, though.

1
2
../src/map.cpp:14:13: error: assigning to 'Tile **' from incompatible type 'Tile *'
    Tiles = new Tile[Width * Height];


If I change Tile **Tiles; to Tile *Tiles; I get more errors, starting with
1
2
no viable overloaded '='
                Tiles[x + y * Width] = new Tile(true, true);


and if I change
1
2
3
4
void Map::InitializeMap() {

    // Emulating a 2D array with a 1D array.
    Tiles = new Tile[Width * Height];


to

 
    *Tiles = new Tile[Width * Height];


It compiles but seg faults.

I really need to sit down and figure out this pointer business, instead of flailing around and hoping I get lucky haha
Last edited on
The statement that allocates the array needs to be like this:
 
    Tiles = new Tile * [Width * Height];

I.e., it needs to allocate pointers to Tile objects, not the objects themselves.
Last edited on
Got it. I appreciate your help!
Hi,

There are differences between Go and C++, I can't help thinking that porting a Go program to C++ may not be be the best way to learn C++.

Go has Garbage Collection for memory management, for which it uses the new function to allocate memory. C++ does not have GC, and using new means one is doing manual memory management with pointers, which are two bad things for a beginner. Even though new does very similar but subtly different things in Go and C++, it means that the wrong approach is followed in C++. The problem with new is that if something throws an exception, neither the destructor nor the delete are reached and memory is leaked. So that is why we prefer to use an STL container which does it own memory management very efficiently.

Instead consider using STL containers which manage memory themselves. For such a small amount of data as this is likely to be you could get away with a plain 2D array on the stack. But it is better to use a container that implicitly puts it's data on the heap. So try this, it's a square 2D std::array:

1
2
constexpr std::size_t Size {10};
std::array<std::array<Tile,Size>, Size> TheMap;


Or if you want to use a 1D representation of a 2D array, try std::vector

Some other topics to research:

References: One can't have a container of references, but there is std::reference_wrapper. Elsewhere references are a good thing, they behave like pointers. Use them for anything which is not a builtin type, unless you are going to use std::move

Smart pointers: std::unique_ptr and std::shared_ptr these embody the RAII concept.


Do try to get rid of your global variables, make the scope of variables as small as possible.

Good Luck !!
Thanks for the insight. I am considering using the STL and smart pointers at some point. I know this isn't the best way to learn C++. I mostly started this tutorial because I wanted to get the basics of BearLibTerminal down and this Go one was the closest thing to C++ I could find. I'll probably give it up at some point, but for now I'm having fun and I am learning things.

I'm back with another question. I'm trying to implement a camera that will follow the player around. The tutorial has this function...

1
2
3
4
5
6
7
8
9
10
func (c *GameCamera) ToCameraCoordinates(mapX int, mapY int) (cameraX int, cameraY int) {
    // Convert coordinates on the map, to coordinates on the viewport
	x, y := mapX - c.X, mapY - c.Y

	if x < 0 || y < 0 || x >= c.Width || y >= c.Height {
		return -1, -1
	}

	return x, y
}


and then later uses it here...

1
2
3
4
5
6
7
func renderEntities() {
	// Draw every Entity present in the game. This gets called on each iteration of the game loop.
	for _, e := range entities {
		cameraX, cameraY := gameCamera.ToCameraCoordinates(e.X, e.Y)
		e.Draw(cameraX, cameraY)
	}
}


How would you implement something like this in C++? I already have the for loop taken care of but the function is confusing. Is that two sets of parameters? Or is one of those what the return values are assigned to?

Sorry for making this a 'help me decipher go code' thread.
Maybe something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Point {
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
};

Point GameCamera::ToCameraCoordinates(int mapX, int mapY) {
    int x = mapX - X;
    int y = mapY - Y;
    if (x < 0 || y < 0 || x >= Width || y >= Height)
        return Point(-1, -1);
    return Point(x, y);
}

void renderEntities() {
    for (auto &e: entities) {
        Point p = gameCamera.ToCameraCoordinates(e.x, e.y);
        e.Draw(p.x, p.y);
    }
}

Of course it's completely untested so you may need to tweak it a little.
And I'm not sure where "entities" is coming from.
Last edited on
Topic archived. No new replies allowed.