Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Katamori

Katamori's programming misadventures - now with videos!

Recommended Posts

With my small programming skills I decided to make a game. This is not the first time but now, I want to do it on a simple way to be able to finish.

I'm doing it with C++, with the support of Allegro libraries.

That's what I have at the moment:



ORIGINAL POST:
As a start, I started to write a random field generator that makes a rectangular character grid in console and put some characters in it. It's gonna be the base of the playing area.

I need you opinions: how to make it more efficient?

There's a problem too: often happens that the grid is not drawn when I run the code. Can someone help me?

Here's the executable (comments are hungarian, sorry): https://www.dropbox.com/s/c65vnk3f34vlv09/Fieldgen0.exe
Here's the source code: https://www.dropbox.com/s/eil3pns9l48zirk/Fieldgen0.cpp

PRE-ALPHA 0.0.1 - BUILD 1
Executable: https://www.dropbox.com/s/mdta9akvywbapqd/Fieldgen0.exe
Source code: https://www.dropbox.com/s/vn6s1z69lk30qr0/Fieldgen0.cpp

PRE-ALPHA 0.0.1 - BUILD 2
Executable: https://www.dropbox.com/s/l7z5r6ivfgkna6p/Fieldgen0.exe
Source code: https://www.dropbox.com/s/pxdo87wa9q62stj/Fieldgen0.cpp

Share this post


Link to post

Weird sequence of actions you do there.

//feltöltés
  
for(j=0;j<25;j++){
    for(i=0;i<25;i++){
         field[i][j]='.';
                     }
    printf("\n");
                 } 
So first every item in the field is a '.', OK.
//kijárat lehelyezése. nem nagy cucc
  
x = rand() % 23 +1;
y = rand() % 23 +1;
    if(field[x][y] != 'X'){
        field[x][y] = 'X';
                            } 
You check for ONE random point in the field if it's not equal to 'X'...which it won't, of course, so essentially you mark one random point as the 'X'. You can skip the if checking, I think.
//random elemek behelyezése, random elszórással
  
for(i=0;i<100;i++){
    x = rand() % 23 +1;
    y = rand() % 23 +1;
    if(field[x][y] != '?'){
        field[x][y] = '?';
                            }else{
                               i= i-1;
                                    }
                 } 
OK, so if I understood you're trying to place 100 unique random '?' marks here, repeating if necessary. Not the most clean way to do it, but it works.
for(k=0;k<4;k++){
    x = rand() % 23 +1;
    y = rand() % 23 +1;
  
// itten gyün 3 ciklus ami ellenőrzi hogy van-e 5x5-ös távolságon belül hasonló elem
// ha nincs, kirajzolja az elemet, ha van, visszalép amíg nem talál olyat ahol nincs
  
    for (j=y-2;j<y+2;j++){
        for (i=x-2;i<x+2;i++){
            if (field[i][j] == 'O'){
                st1 = true;
                                    }
            if (field[i][j] == '|'){
                st2 = true;
                                    }
            if (field[i][j] == '-'){
                st3 = true;
                                    }
  
                            }
                        }
  
    if(st1 == false && st2 == false && st3 == false && x<25 && y<25 && x>0 && y>0){
        field[x][y] = 'O';
        field[x-1][y] = '-';
        field[x+1][y] = '-';
        field[x][y-1] = '|';
        field[x][y+1] = '|';
                                                    }else{
                                                        k= k-1;
                                                        }
                 }
  
If I understood correctly, you're trying to place 4 door/portal-like elements like this:
---
|O|
---
on your grid. You place an element centered on [x][y] iff there are no '|', '-' or 'O' within a Moore distance of 2 from [x][y] (the iterator you designed is a radius-2 Moore/Tchebychev iterator), otherwise you try again.

The first obvious problem I see here is that the st1,st2,st3 variables are not reset to some default value at the beginning of a loop, so if at some point they are all set to true there's no way to set them back to false.

Second, the iterator might go out of bounds (1+ rand()%23 results in values of x,y:[1,23], and if you subtract 2 from x or y it might go out of bounds.

Third, since you said that "sometimes" it doesn't draw the grid, it would be useful to specify exactly WHICH portions are not drawn. All of them? Just the "portals"? What values of x,y and/or rand() cause this? Also, modifying the loop variable "k" is a bad idea: it might go negative SEVERAL times, and in the end either place more than 4 "portals" on the grid, or even underflow under extreme circumstances.

There are also easier ways to check if a "portal" is already placed: just check if a grid cell has an 'O', and/or decide beforehand where you want your portals placed (come up with 4 pairs of coordinates) and make sure than each pair has a Moore distance of >=2 or more than the others.

Also, I don't get this bit:
void write(char tomb[25][25]){
        int row,col;                           //row = sorok; col = oszlopok
        for(col=0;col<20;col++){
            for(row=0;row<20;row++){
                printf("%c ",tomb[row][col]);
                                    }
        printf("\n");
                                }
                            } 
Why do you only write the first 20x20 cells of the grid, if previously you computed a 25x25 one? It's not like you used a part of them as a safety border, either, though you seem to constrain certain x,y calculations to a [1,23] range, as noticed before. Perhaps you meant to use a 22*22 grid? In that case you must map a [1...23]range to [0...22] range in write()

Share this post


Link to post

Maes:

To the second part: thanks! I didn't even notice that the condition is unnecessary.

To the third part: I thinks that's OK. The condition clears every cases when an already replaced unit would be replaced again. If the x,y unit IS NOT a ?, then put one, if it IS, the go one step back and repeat until it isn't. Theoretically i= i-1 works even if "i" is the condition "looper number", but I have some doubts.

To the fourth part: that was the main problem. When that part came, then I think an infinite loop happened somehow. While I waited for the answer, I realized the problems of st1-2-3 variables.

Also, you misunderstood something. I want to do somehing like this:
|
- O -
|

Anyway, my biggest problem now is that I don't know, how to go back. Even you say that I shouldn't change the loop modifier, but I absolutely lost and couldn't find any other ways to restart the loop before the end.

To the fifth part: that was my mistake. The original area van 20x20 then I enlarged it to 25x25 - I just forget to change the function.

Thanks for the help, now I have a new problem. I made things simplier. Now I only want to put characters randomly, with the rule that they can't be adjecent with the same type of characters. I did it this way:

void *memchr( const void *buffer, int ch, size_t count );

[...]

for(k=0;k<50;k++){
    x = rand() % 23 +1;
    y = rand() % 23 +1;

    for (j=y-1;j<y+1;j++){
        for (i=x-1;i<x+1;i++){
            if(memchr(field,'=',i*j) == NULL){
                        st=false;
                                   }else{st=true;}
                                   st=false;
                             }
                        }


    if(st == false){
        field[x][y] = '=';
                                                    }else{
                                                        k=k-1;
                                                        }
                 }
But "="s are still adjacent sometimes. How should I do it?



fraggle: I did my best. I think it's still not so hard to read.

Share this post


Link to post

Since a lot of the work you're doing is placing down a number of objects with a set shape and dimensions while trying to avoid collisions/ovelaps, you really should move from a cluttered assortment of for-loops, each with different rules and implementations, to a more general solution.

E.g. define objects in an array, set their dimensions, pack everything together in a struct/class, and make a function which takes a grid and an object as an input/template plus parameters such as desired number, whether overlap with existing stuff on the map is allowed etc. and solve the collision problem generally (e.g. specify ovelap Moore distance: 0 means objects can touch each other, 1 that they need at least one unit between them in either dimension, etc.)

Also, FWIW, the memchr method might not work correctly for 2D arrays like field: there might be padding bytes between different lines to the next multiple of 4 or 8, depending on the allocator, compiler, CPU architecture etc. It's an oversight to try accessing it as if it were a "flat" 1D array assuming that successive lines will just form one continuous "string" of characters. This is only true if you defined it as field[25*25], not field[25][25].

Share this post


Link to post

Maes: Firstly it seemed too complicated, but it was only because of the language differences. (it's not the only one, e.g. in my language, loops are called "cycles")

Anyway, sounds good. I'm gonna try it. Thanks! Results are coming soon.

Vaporizer: I was learnt to get rid of int before main(), that's why I did this way.

Share this post


Link to post

Next problem. I made this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

void exitmaker(char tomb[25][25],int a, int b){
    tomb[a][b] = '#';
                                            }

main(){

srand(time(NULL));

char field[25][25];
int i,j,x,y;

x = rand() % 23 +1;
y = rand() % 23 +1;
exitmaker(field,x,y);

for(j=0;j<25;j++){
    for(i=0;i<25;i++){
         field[i][j]='.';
                     }
                 }

        for(j=0;j<25;j++){
            for(i=0;i<25;i++){
                printf("%c ",field[i][j]);
                                    }
        printf("\n");
                                }
}
and it still doesn't work, although it should be! What did I do wrong? The "." field is OK, but there's no "#".

Share this post


Link to post

Because you're overwriting the '#' with the '.'s, maybe?

Also, as a general rule, it's poor practice to define functions with fixed-size array arguments in C/C++. I suspect a previous exposure to Pascal/Delphi, right? ;-)

All of them decay to pointer types anyway (like char*, char** etc.) so it makes more sense to rewrite the function such as:

void exitmaker(char* tomb,int a, int b,int width){
    tomb[a+b*width] = '#';
    }

which will work, since static multidimensional arrays in C/C++ are one contiguous block of memory.

EXCEPTION to this rule: multidimensional arrays allocated with the new operator. Those might not always play nice, as I pointed out before.

Even if you really HAVE to write it as a multidimensional array, at least do the following:
#define GRID_WIDTH 25
#define GRID_HEIGHT 25
[code]void exitmaker(char[GRID_WIDTH][GRID_HEIGHT] tomb,int a, int b){
    tomb[a][b] = '#';
    }
It will make your life easier ;-)

Share this post


Link to post
Katamori said:

Possible.


Not just "possible", you're actually creating the exit BEFORE you fill the field with '.'s. How is it supposed to be there after that?

Share this post


Link to post

If you describe exactly what you intend the code to do in english I can try it in python for fun, if its not too complicated/hard, which can probably be translated to c or whatever you're using pretty straightforwardly.

Share this post


Link to post
gggmork said:

If you describe exactly what you intend the code to do in english I can try it in python for fun, if its not too complicated/hard, which can probably be translated to c or whatever you're using pretty straightforwardly.


Check the download links now; I have refreshed them.

Now I did the whole random generation thing with the way Maes have suggested. The only problem now is that if I want to generate this:

. . . . .
. . $ . .
. $ O $ .
. . $ . .
. . . . .
...in that case I can see a kind of hole with different characters. Should I use pointer? Or what's the problem?

Share this post


Link to post

Cool, seems interesting. I think I may try something similar sometime later.

Share this post


Link to post

PRE-ALPHA 0.0.1 - BUILD 2
Executable: https://www.dropbox.com/s/l7z5r6ivfgkna6p/Fieldgen0.exe
Source code: https://www.dropbox.com/s/pxdo87wa9q62stj/Fieldgen0.cpp

This is quite playable. I have only one problem: the "D"-s are moving too much sometimes. I have absolutely no idea, how is it possible, since I only allow them to move one unit to a straight direction (which means that I didn't do any diagonal movings).

Maes, gggmork, anyone? Any ideas?

Share this post


Link to post

OMG IT'S ASCII MARRY ME! Anyway, I don't know if anything other than the D moving is supposed to happen, but I'm getting a nice warm vibe from the visuals already. I don't know how your spriting is, but if you're looking for someone to help you with working on a tileset eventually, I could volunteer to put down some placeholders (I'm by no means talented in this area).

Also, it works after round 50 now.

Share this post


Link to post

It's just a base, because I can't do real graphics yet, and I also think that it's enough yet - graphics would make me confused while I develop.

Of course it will get real graphics, ASCII is for test only. =)

Share this post


Link to post

I wrote Tetris once like that. Did it in Java, though. Harder to play when the whole screen scrolls up with every action. Never did learn how to build sexy ASCII programs that do everything in place (think of things like pine, Norton Commander, MS-DOS Editor, QBasic, etc.).

Anyway, I can't see the bug right away with a quick skim, but I'm with fraggle: your code isn't formatted consistently, let alone according to any kind of standard. Reading it is like unraveling a hairball. This isn't just a programmer snob thing; it helps you debug stuff and helps others see what you're doing more quickly.

The getParity() function you borrowed is a decent example: readable camel-case name, consistent indentation, way-too-gratuitous tabbing on the second line in the loop, etc. All of us with some experience could write essays about coding style here, but here's a link instead. This guy analyses John Carmack's Doom III code style. It explains why he does things a certain way better than I can. The parts about spacing and constant parameters are good reads. After that you might find it a bit intimidating. I'm not 100% convinced Carmack's extra horizontal spacing is always necessary, but it does make his stuff easy to read.

Maes already mentioned you might want to try and avoid defining fixed-size arrays in function headers. His examples are good, but he didn't elaborate on the reasons you should do it like he does. You keep rewriting numbers throughout your functions. Here's an excerpt from your generateit() function with Maes-style inputs and Carmack-style formatting:

#define GRID_WIDTH 75
#define GRID_HEIGHT 25

...

char generateit(char tomb[GRID_WIDTH][GRID_HEIGHT], struct shapes theirlist, int max_chasms){
    int i = 0, j, k = 0, found, x = 0, y = 0;
  
//kijárat
    x = rand() % ( GRID_WIDTH - 1 ) + 1;
    y = rand() % ( GRID_HEIGHT - 1 ) + 1;
    tomb[x][y] = theirlist.exit;
  
//szakadékok
  
    for( k = 0 ; k < max_chasms; ){
        x = rand() % ( GRID_WIDTH - 1 ) + 1;
        y = rand() % ( GRID_HEIGHT - 1 ) + 1;
        if ( tomb[x][y] == theirlist.dirt ){
            tomb[x][y] = theirlist.chasm;
        } else {
            continue;
        }
        k++;
    }

...
}
The bog thing here is the magic numbers go away. All those 74s and 24s go away and are instead defined by the size of your grid. You avoid bugs due to typos (84 would be bad) and when you want to change the size of your tomb you just change the numbers in one place. (Maes' first example is better for that in the long run.) Your movement bug might be due to a mis-copied number, for example.

Also, when you use for(k=0;found != true;){...} you ought to be using while(found == false) {...} or while(!found){...} instead.

Share this post


Link to post
Aliotroph? said:

I wrote Tetris once like that. Did it in Java, though. Harder to play when the whole screen scrolls up with every action. Never did learn how to build sexy ASCII programs that do everything in place (think of things like pine, Norton Commander, MS-DOS Editor, QBasic, etc.).


By using just the standard console output in Java, you can't really have the same degree of control over text output that you had with MS-DOS and languages like QBASIC or C "standard" library extensions like Borland's <conio.h>, let alone expect it to be X-platform.

You really need a custom Component that handles colored text and emulates absolute cursor location and foreground/background color settings, kinda like the ENDDOOM screen visualizer used in Choco/prBoom.

Many people complained about :
Code formatting


And rightly so. Unless you're using notepad, EDIT for MS-DOS or vi, there's no excuse to have code so badly formatted (or rather, non-formatted). But then again there's no excuse for using any of those ;-)

Most IDEs and advanced editors like Notepad++ include an auto-formatting option, at least use that FFS.

Katamori said:

D's moving too much


Well, there's nothing in the code preventing a single D from moving more than once, because you don't have a separate list of D object but a single memory structure, aka, the playfield itself. You scan the playfield top-down and left-right in life(...), so if you happen to move a 'D' down or right, you will encounter it again as the scanning progresses, causing "scan-dragging".

There are several ways around that, each with its own advantages and disadvantages:

  1. Keeping separate lists of monsters/items/actors/obstacles. Very flexible and long-term solution, but will need an almost complete re-engineering of your code.
  2. Make a scratch copy of the playfield in life(...), and write any modifications to that copy, while scanning the original. This way, you avoid "scan-dragging". In the end, copy the scratch copy to the original. It's easier than keeping lists of objects, but it costs more computing power and memory.
  3. In life(...), try to count how many 'D's there are, and introduce some mechanism to move each of them only once....it's actually a poor way of implementing solution #1 in disguise, and more complex, so I'd go with the real thing instead.

Share this post


Link to post

Aliotroph?, thank you very much! As you explained, I could understand, what was the problem with the format of my code. I made it a bit better - I think this is still not perfect, but maybe easier to read.

Also, defining height and width at the very beginning is an extremely great and useful idea! How could I miss it...?!

Maes, thank you too for the help, but I could find the right way for it. Actually, the game is gonna be a special kind of cellular automaton, at least I want to.

Share this post


Link to post
Katamori said:

AActually, the game is gonna be a special kind of cellular automaton, at least I want to.


Well, to understand the problem at hand, try implementing your own version of Conway's Game of Life: the need to clearly distinguish between the "before" and "after" states without altering the "before" state prematurely, will be more than evident.

E.g. what would happen if you applied the death/birth/survival rules in Conway's Game of Life at one cell at a time and immediately altering the playboard before moving on to the next cell?

Share this post


Link to post
Maes said:

Well, to understand the problem at hand, try implementing your own version of Conway's Game of Life: the need to clearly distinguish between the "before" and "after" states without altering the "before" state prematurely, will be more than evident.

E.g. what would happen if you applied the death/birth/survival rules in Conway's Game of Life at one cell at a time and immediately altering the playboard before moving on to the next cell?


Oh, holy sh...I didn't think about it...

Maybe duplicating the original array somehow?

Share this post


Link to post
Katamori said:

Maybe duplicating the original array somehow?


That would be solution #2 of my list, and is probably the best method for cellular automata, especially beyond a certain population and grid size where lists would not give any space advantage and add too much processing overhead.

OTOH, Doom uses lists because the population is much smaller than the grid size, which in turn is very large. However this has the side effect that monster processing/thinking is NOT independent of order nor side-effect free (e.g. if monster A kills monster B first in a tic using a hitscan or melee attack, then monster B can't possibly fight back). In a cellular automaton, OTOH, progression between states must be free of side-effects and applied with equal priority to all cells.

Share this post


Link to post

A n00b question is coming: what kind of list do you mean? Sorry, I learnt programming at the univwersity in my native language, so sometimes I don't know the original English name of elements

Share this post


Link to post

Hahaha thanks! :D

However it seems a bit complicated so I think I'd stay with the copying solution =)

Share this post


Link to post

Something else I've made - but actually, not so different.

You can move in an ASCII grid. You can't leave the visible area, and also, you can move over some object. This one was really really hard to crate!

Executable:

https://www.dropbox.com/s/k7xmq9buakksvb5/fallenstar.exe

Other files are available here, cause I started to use classes.

https://www.dropbox.com/sh/w7afm9plnvdipr0/Ei0ZW-Nwjv

Share this post


Link to post

Didn't look at the code but modulus can be useful for this type of thing if you want the grid "universe" to wrap around pac-man style so one side leads back to the opposite.

Like if the gridWidth is 50 (0 through 49), and you are on index 49 and you want to go to the right 3 spaces, that's 49+3=52, which is bigger than gridwidth. But 52 % 50 (modulus gridWidth) is 2, which is where it would be if you "warped" across to the other side.

Spam, I did cellular automata ish stuff in ACS here: http://www.doomworld.com/vb/showthread.php?s=&postid=1158967#post1158967
I only know acs, not c, but I guess its similar.

When I tried making "graphic" dippy game stuff using text only, I'd print a bunch of newlines, then the grid, so each next frame will visually appear right where the last frame was. Eventually it makes tons of text output so probably slows down since it doesn't "erase" previous frames. To do that I'd use SDL and make a jpg or whatever for each letter, then blit and erase as normal, but might as well use more interesting images at that stage.

Wondering if notepad++ or whatever has an option:
while(bla)
{
@
}
where like, right after you type the while it will auto-type the first and last bracket and put the cursor indented between them at the @ automatically. That's kinda annoying in acs. Python doesn't have brackets or semicolons.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×