Ruby to C++

Programming, for all ages and all languages.
Kon-Tiki

Ruby to C++

Post by Kon-Tiki »

I'm trying to turn some Ruby code into C++, but'm so stuck at some things. This's the worst one:

Code: Select all

$field[x1..x2].each do |x|
valid = false  if x[y1..y2].include?(" ") == true
Anybody know how to interprete this, or how to translate it to C++?
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Ruby to C++

Post by Candy »

What should the ruby code do?
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:Ruby to C++

Post by Solar »

Hmmm... let's see how intuitive Ruby syntax is. (Haven't seen Ruby "in action" before.)

I'd say...

For each element in field, starting with index x1 and ending with index x2...

...do |x|? Mathematically, that would be "absolute value", but I see that x is referenced further down, so it's probably some creation of a data object. (Note: minus 5 points for Griffindor, erm, Ruby, for using mathematic syntax out-of-context.)

...the variable valid shall be false if... (minus another 5 points for postfix "if"...)

...any element of the x created above, starting with index y1 and ending with index y2, includes a space. (Minus 10 points for the author of the code for using obfuscated syntax - doesn't Ruby have a 'not' operator?)

Now, field could be a sequence of strings and the whole shebang could be some kind of substring search. But who knows, with a typeless language? 8)
Every good solution is obvious once you've found it.
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

I've figured it out pretty much like that, and the postfix if and absolute value threw me off, first. What it means, is this:

First of all, field's a double array.

For every index of field[] between x1 and x2, make an array x that contains the second dimension of this index.
If there's at least one index in x[] between y1 and y2, which has as value " ", set valid to false.

This's still a guess, but at least it's a guess some people've made along with me. The total of the code still works like a mess, though (loops that take too long, runtime crashes, etc) Translation a Ruby script to C++ isn't done that easily.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:Ruby to C++

Post by Solar »

OK, so we have a two-dimensional field of strings, and we are looking for a string in the second dimension that contains a space, right? Or is it a three-dimensional field, and we're looking for a string in the third dimension that is " "?

I will assume the former, and also assume that "valid" is pre-initialized to "true".

This is more or less a first for me, posting code that hasn't been tested before posting, so take with a grain of salt.

C:

Code: Select all

#include <stdbool.h>
#include <string.h>

inline foo( char *** field, size_t x1, size_t x2, size_t y1, size_t y2 )
{
    for ( size_t i1 = x1; i1 <= x2; ++i1 )
    {
        for ( size_t i2 = y1; i2 <= y2; ++i2 )
        {
            if ( strchr( field[i1][i2], ' ' ) != NULL )
            {
                return false;
            }
        }
    }
    return true;
}
I could come up with a sophisticated C++ version that uses two functors and for_each(), but I think you're better off doing the simple thing:

Code: Select all

#include <vector>
#include <string>

using std::vector;
using std::string;

inline bool foo( vector< vector< string > > & field, size_t x1, , size_t x2, , size_t y1, , size_t y2 )
{
    for ( size_t i1 = x1; i1 <= x2; ++i1 )
    {
        for ( size_t i2 = y2; i2 <= y2; ++i2 )
        {
            if ( field[i1][i2].find( ' ' ) != std::npos )
            {
                return false;
            }
        }
    }
    return true;
}
If you want to do bounds checking, don't use vector::at() here, but rather check that x1 < x2 <= vector::size() and y1 < y2 <= vector::size() before entering the respective loop.
Every good solution is obvious once you've found it.
mystran

Re:Ruby to C++

Post by mystran »

Code: Select all

$field[x1..x2].each do |x|
   valid = false  if x[y1..y2].include?(" ") == true
end
Ok, I can program Ruby, at least a bit, and this seems like pretty basic code to me.

Take the range x1..x2 from $field, and for each element do the block of code (where the element is called x):

Set valid to false if the range y1..y2 within x includes the string " ". What this does depends on whether x is a string or an array, so you need to know what $field actually contains.

Check Ruby manuals for details about the ranges and includes? once you know the types involved. Duck typing means you don't know what some code does before you know what object it deals with.


Btw, that "== true" makes no sense. The original programmer might be uncompetent.
It's an example of the (((true == true) == true) == true) syndrome which does no good.

Whether translating Ruby to C++ is a good idea is another question. Ruby being descendant of SmallTalk is more or less in the opposite end of what is called "object oriented" and relies a lot on duck-typing and closures, which are pretty hard to model in C++.
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

I noticed ;) Plus it's hard as hell to figure out where which loop stops. I already seem to've closed a for-loop too soon. Aside from that, it works fairly good so far. Translated half of the code up til now and edited it slightly to stop it from creating index out of bound crashes and such. Second part: drawing the corridors from one room to another.

After all this, I'll really need to clean the code up. It's a bit of a mess at the moment ::)
User avatar
df
Member
Member
Posts: 1076
Joined: Fri Oct 22, 2004 11:00 pm
Contact:

Re:Ruby to C++

Post by df »

eaiiie i love ruby. dont go back to c++...
-- Stu --
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

Donedonedonedonedonedonedonedone!

Image
Image
Image
Image
Image
Image

S stands for Start, F for Finish. Will fix that up to < and > now. But it works! I finally got a random dungeon generator :D

That Ruby's pretty easy to translate once you got the hang of it :)
User avatar
df
Member
Member
Posts: 1076
Joined: Fri Oct 22, 2004 11:00 pm
Contact:

Re:Ruby to C++

Post by df »

nice
-- Stu --
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

The corridors sometimes overwrote the entrance, so I fixed that. Now it doesn't connect some corridors, and often leaves certain rooms unattached (which's especially painful if either entrance or exit's in them). Strangely enough, it still does that now, but to a lesser extend, after I removed all additions I made, bringing it back to the code I took those screenshots of.
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

All fixed! Just shows you shouldn't program at 3am ::) C++ source's attached to this post. I'll attach the Ruby code to the next one. All comments're in Dutch, but to translate those as well... I'll just translate the main idea behind the logic (which'll be the next post)

Edit: For better dungeons: change line 211 to this:

Code: Select all

    if ((randomizer(2) == 1) && (x_backup != x1) && (x_backup != x2)) {
(randomizer(2) == 1 instead of == 0)
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

0: Create the variables you'll need

1: Create the map-array (2D array)

2: Pick the amount of rooms you'll make. A bit of statistics can be fun here (normaldivision etc.)
Here it's done by taking a random number between 1 and 85, then dividing those 85 possibilities in ranges and assigning each range to an amount of rooms.

You can of course always take a random number between 1 and 8, which'll get you an amount of rooms between 1 and 8, but then you'll have as much chance on 1 room as on 4.

3: Do this for each room:
1) Make 4 random numbers x1, x2, y1, y2. Make sure that
x1 < x2 < right border of the field (x1 is left of x2)
y1 < y2 < bottom of the field (y2 is below y1)
With these 4 numbers, the room'll be defined:
(x1y1-x1y2-x2y1-x2y2)
2) Check if you're not overlapping an existing room.
This goes by checking if between x1 and x2 for each y between y1 and y2, there're no walls or roads in the way.
3) Check if the room between the four points's good enough. This step can be left out, I just find big or long, small rooms ugly. Rooms smaller than 2 squares are forbidden too in case there's only 1 room (otherwise, there's no room for both a start and a finish)

Along with that, it's possible that, if you want to make too big and/or too many rooms, that there's no space for the last rooms, so the thing'll get stuck in a loop. This can be solved by making a paralel thread which redoes the entire floor (all rooms and amount of rooms, etc) over again after waiting a second or two)

Let 1)-3) run until 2) is OK and 3) is OK as well.

4) Empty the contents of your room. (Replace the "#" by a ".")

5) If the room you just created, is the first room, a starting position has to be placed at random within this room.

6) If the room you just created, is the last room, then the finish has to be placed at random within this room (but not on the starting position in case there's only 1 room)

7) Pick a random point within the room. If the current room isn't the first one, a corridor has to be placed to the previous room.
Go from within the chosen random point horizontally to the crossing of the vertical line which goes through the randomly chosen point of the previous room, or the other way around (randomly chosen).
x1y1#|######x2y1 |
# Old room # |
# | # x1y1#|##############x2y1
--------@------------------------X----- #
x1y2#|######x2y2 # | New room #
| # | #
---X------------------------@-------------------------------
| x1y2#|##############x2y2
| |

@ : random chosen point


Make a random corridor from @ to @ over X. Which X you choose randomly.

Cool Colour the corridor away (replace each point with a ".", except beginning- and endpoints)

9) Replace all backup-variables with the variables of the current room and restart the loop.

4: Print the floor to the screen
YeXo

Re:Ruby to C++

Post by YeXo »

Good that it works now. A few comments on your code:

Code: Select all

    int nr_rooms;
    int random;
    random = randomizer(85);

    if( (random >= 5) && (random <= 10) ) nr_rooms = 2;
    if( (random >= 11) && (random <= 20) ) nr_rooms = 3;
    if( (random >= 21) && (random <= 30) ) nr_rooms = 4;
    if( (random >= 31) && (random <= 40) ) nr_rooms = 5;
    if( (random >= 41) && (random <= 50) ) nr_rooms = 6;
    if( (random >= 51) && (random <= 60) ) nr_rooms = 7;
    if( (random >= 61) && (random <= 70) ) nr_rooms = 8;
    if( (random >= 71) && (random <= 81) ) nr_rooms = 9;
    if( (random > 81) || (random < 5) ) nr_rooms = 10;
What is this code for? Why don't you just do
nr_rooms = randomizer(10);

Code: Select all

 while (valid == false) 
This is (in my opinion) better:

Code: Select all

 while (!valid) 

Code: Select all

      while(temp_x<=x2){
       int j=y1;
         while(j<=y2){
          if (field[temp_x][j] == ".") valid = false;
           j++;
         }
        temp_x++;
      }
If you change it to the next it will be more efficient:

Code: Select all

      while(temp_x<=x2 && valid){
       int j=y1;
         while(j<=y2){
          if (field[temp_x][j] == "."){
              valid = false;
              break;
          }
           j++;
         }
        temp_x++;
      }
I must say your code is good readable (especially with those Dutch comments :P). Once more, I'm only trying to help you improving your code.
Kon-Tiki

Re:Ruby to C++

Post by Kon-Tiki »

Thanks :) I know the code could use some cleaning up, and I appreciate the suggestions. Will be making them after this post. The first one (the load of ifs), is the statistic stuff that's explained in nr. 2 of the algorithm's explanation. Aside from that one, it does seem to be more efficient with the other suggestions.

Edit: All done
Post Reply