3

I'm translating such fragment of this Python file to C++:

SIDE = 3
LINES = []
for y in range(SIDE):
    row = tuple((x, y) for x in range(SIDE))
    LINES.append(row)
for x in range(SIDE):
    col = tuple((x, y) for y in range(SIDE))
    LINES.append(col)
LINES.append(tuple((x, x) for x in range(SIDE)))
LINES.append(tuple((SIDE - x - 1, x) for x in range(SIDE)))

LINES holds (x, y) coordinates of possible lines in Tic Tac Toe game. So for SIDE = 3 it holds:

[((0, 0), (1, 0), (2, 0)), 
 ((0, 1), (1, 1), (2, 1)), 
 ((0, 2), (1, 2), (2, 2)), 
 ((0, 0), (0, 1), (0, 2)), 
 ((1, 0), (1, 1), (1, 2)), 
 ((2, 0), (2, 1), (2, 2)), 
 ((0, 0), (1, 1), (2, 2)), 
 ((2, 0), (1, 1), (0, 2))]

SIDE value can change.

What I've tried

Performance is crucial (that's why I reached for C++), so I would like to calculate LINES only once. Thus, I've chosen to implement LINES as a static member of the class TicTacToeState.

I started with such code:

static char init_lines() {
    return 'a';
}

class TicTacToeState {
    static char LINES;
};

char TicTacToeState::LINES = init_lines();

It works. How to change LINES to an array? Maybe vector will be better? With pairs?

Maybe static member is not the best choice, maybe there is an easier way?

How would you translate it to C++?

We know the size of LINES, it's always 2 * SIDE + 2.

Special requirement

All C++ code must be in one .cpp file, no headers. Why? Because this is fragment of a library for bot competitions and it's typical that you can submit only one file.

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
  • you can just return a `char*` pointing to the first element of the array, that's how you would normally return arrays in C++. Check out http://stackoverflow.com/questions/5660527/how-do-i-return-a-char-array-from-a-function or http://stackoverflow.com/questions/3473438/return-array-in-a-function – R Nar Oct 14 '15 at 23:43
  • @RNar You are missing the point of the question. I'm not asking how to return an array from a function, I'm asking how to initialize static member array with anything I would like. – Adam Stelmaszczyk Oct 14 '15 at 23:47
  • my bad, i thought you were just using char as an example. – R Nar Oct 14 '15 at 23:48

2 Answers2

2

In C++ you can initialize static array members using group initialization

    static int a[10] = {5}; //this will initialize first position item with 5 and rest with 0s
    static char b[2] = {'b', 'b'};
    static int c[2][2] = { {1,1}, {1,2} };

    int main()
    {
        cout<< a[0] << endl; //output: 5
        cout<< a[1] << endl; //output: 0

        cout<< b[0] << endl; //output: b

        cout<< c[0][1] << endl; //output: 1
    }

Although the fact is you need to know size of the array not like in Python's list that are dynamically


If you need to insert to the table values calculated dynamically the best way to do this is to create factory method

    static int** fact(int width, int height)
    {
        int** a;

        a = new int*[width]; //we can do it when it is DYNAMIC array! 

        a[0] = new int[height];
        a[1] = new int[height];

        for(int i = 0; i < width; i++)
            for(int k = 0; k < height; k++)
                a[i][k] = i*k;

        return a;
    }

    static int** c = fact(2, 2); //you can call it with your SIDE var

    int main()
    {
        cout<< c[1][1] << endl; //output: 1
    }

Of course you can process it in loops

The same approach will be proper when you will decide to use std Vector class which is equvalent of Python's dynamic list

m.antkowicz
  • 13,268
  • 18
  • 37
  • There is a bigger problem - I can't initialize it with { } array initializer and constant values given in it. As you can see in Python code I need to dynamically calculate the coordinates based on `SIDE` value. – Adam Stelmaszczyk Oct 14 '15 at 23:50
  • Why didn't you use a `vector`? – 5gon12eder Oct 15 '15 at 00:01
  • @m.antkowicz I quite like that, cheers... I also updated the question - we know the size of the array, it's always `2 * SIDE + 2`. – Adam Stelmaszczyk Oct 15 '15 at 00:02
  • see the code updated; @5gon12eder - I'm not sure but as far as I know C++ I guess that arrays provides better performance due to shorter time of accessing elements and (if you know the size at first) this would be the argument not to use STL Vector collection. Please correct me if I'm wrong cause I can be – m.antkowicz Oct 15 '15 at 00:16
  • 1
    Accessing elements of a `vector` is just as fast as accessing elements of an array, if you use `v[i]` syntax. The difference will be when allocating and deallocating, but if the size isn't known at compile time you can't avoid that. – aschepler Oct 15 '15 at 00:19
  • 1
    @m.antkowicz *Stack-based* (or *static*) arrays are faster than a `std::vector` because you don't have to allocate memory for them. (Note you can use `std::array` which has equal performance in this case, though.) Once you reach for `new`, you can – at the same cost but without all the trouble – use a `std::vector`. – 5gon12eder Oct 15 '15 at 00:19
  • 1
    @m.antkowicz They should be comparable in performance for accessing/writing. Under the hood, a vector is implemented as a dynamically allocated array after all. It's only slower when you resize it above what it had already reserved in memory. But then again, you don't actually have that functionality natively with arrays. – NoseKnowsAll Oct 15 '15 at 00:20
  • Ok I see - that you all very much for explanation! @AdamStelmaszczyk - as you see you can create the factory function version with Vector class as it seems to be more convenient in using – m.antkowicz Oct 15 '15 at 00:23
1

I suppose you could do this using a lambda function like this:

#include <vector>
#include <iostream>

const auto SIDE = 3U;

struct coord
{
    unsigned x;
    unsigned y;
    coord(unsigned x, unsigned y): x(x), y(y) {}
};

static const auto lines = [] // lambda function
{
    // returned data structure
    std::vector<std::vector<coord>> lines;

    for(auto y = 0U; y < SIDE; ++y)
    {
        lines.emplace_back(); // add a new line to back()
        for(auto x = 0U; x < SIDE; ++x)
            lines.back().emplace_back(x, y); // add a new coord to that line
    }

    for(auto x = 0U; x < SIDE; ++x)
    {
        lines.emplace_back();
        for(auto y = 0U; y < SIDE; ++y)
            lines.back().emplace_back(x, y);
    }

    lines.emplace_back();
    for(auto i = 0U; i < SIDE; ++i)
        lines.back().emplace_back(i, i);

    lines.emplace_back();
    for(auto i = 0U; i < SIDE; ++i)
        lines.back().emplace_back(SIDE - i - 1, i);

    return lines;
}(); // NOTE: () is important to run the lambda function

int main()
{
    for(auto const& line: lines)
    {
        std::cout << "(";
        for(auto const& coord: line)
            std::cout << "(" << coord.x << ", " << coord.y << ")";
        std::cout << ")\n";
    }
}

Output:

((0, 0)(1, 0)(2, 0))
((0, 1)(1, 1)(2, 1))
((0, 2)(1, 2)(2, 2))
((0, 0)(0, 1)(0, 2))
((1, 0)(1, 1)(1, 2))
((2, 0)(2, 1)(2, 2))
((0, 0)(1, 1)(2, 2))
((2, 0)(1, 1)(0, 2))
Galik
  • 47,303
  • 4
  • 80
  • 117