I am trying to make a dungeon generator for a roguelike, using a BSP like the one described here: Basic BSP
I am a bit new to tree structures, and I have been working on this for a while now and really can't seem to figure out where I am going wrong. For now I have it so it will just print out all "#"s to represent the room dimensions of each leaf node so I can see if it is working. I keep getting segmentation faults and I am really not sure where I am going wrong at this point. If anyone could take a look at my code and give me some advice I would greatly appreciate it. Thank you!
#include <ncurses.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define MIN_ROOM_HEIGHT 5
#define MIN_ROOM_WIDTH 10
struct tree
{
int max_x, min_x;
int max_y, min_y;
struct tree *Right_Down, *Left_Up;
};
typedef struct tree room;
room create_rooms(room **map, WINDOW *board, int max_x, int min_x, int max_y, int min_y)
{
room *temp = NULL;
if(!(*map))
{
temp = (room *)malloc(sizeof(room));
temp->Left_Up = temp->Right_Down = NULL;
(temp)->max_x = max_x;
(temp)->min_x = min_x;
(temp)->max_y = max_y;
(temp)->min_y = min_y;
*map = temp;
}
/* room structures for smaller rooms this room will be
* split into, Left/Up or Right/Down, depending on whether
* horizontal or vertical split was chosen.
*/
// room newLeft_Up, newRight_Down;
/* Position to split at for horizontal and vertical */
int split_horiz, split_vert;
/* Divide room into 2 smaller rooms, unless doing so
* would make them below minimum room size
*/
int split_type = rand() % 2;
int stop = 0;
/* 1 is Horizontal Split */
if(split_type == 1)
{
if((*map)->max_y > MIN_ROOM_HEIGHT+4)
{
while(stop == 0)
{
if((split_horiz = rand() % ((*map)->max_y-2)) < MIN_ROOM_HEIGHT+4);
{
stop = 1;
}
}
create_rooms(&(*map)->Left_Up, board, (*map)->max_x, (*map)->min_x, split_horiz-1, (*map)->min_y);
create_rooms(&(*map)->Right_Down, board, (*map)->max_x, (*map)->min_x, (*map)->max_y, split_horiz+1);
}
}
/* 2 is Vertical Split */
else
{
if((*map)->max_x > MIN_ROOM_WIDTH+4)
{
while(stop == 0)
{
if((split_vert = rand() % ((*map)->max_x-2)) < MIN_ROOM_WIDTH+4);
{
stop = 1;
}
}
create_rooms(&(*map)->Left_Up, board, split_vert-1, (*map)->min_x, (*map)->max_y, (*map)->min_y);
create_rooms(&(*map)->Right_Down, board, (*map)->max_x, split_vert+1, (*map)->max_y, (*map)->min_y);
}
}
char tiles[(*map)->max_y-1] [(*map)->max_x-1];
int i, j;
/* Print room tiles if this is a leaf node */
if((!((*map)->Left_Up)) && (!((*map)->Right_Down)))
{
for(i=(*map)->min_y; i < (*map)->max_y-1; i++)
{
for(j=(*map)->min_x; j < (*map)->max_x-1; j++)
{
tiles[i][j] = '#';
wmove(board, i, j);
wprintw(board, "#");
}
}
}
wrefresh(board);
}
int main()
{
/* ncurses initializations */
initscr();
start_color();
keypad(stdscr, TRUE);
refresh();
noecho();
/* counters */
int i, j;
/* seed srand for later use */
srand(time(NULL));
/* Initialize Game Board */
WINDOW *board;
board = newwin(LINES, COLS, 0, 0);
box(board, 0, 0);
room *map;
map = NULL;
create_rooms(&map, board, COLS-1, 1, LINES-1, 1);
wrefresh(board);
/* sleep for 5 secs */
sleep(5);
/* close ncurses */
endwin();
return 0;
}
Related
I posted a similar question to this one a few weeks ago where I had trouble finding the data race in my N-queens program using pthreads in C. Why is my multithreaded C program not working on macOS, but completely fine on Linux?
I got a few suggestions in the comments sections of the post and I really tried my best to make corrections based on them. I sat with the suggestions a few days, changed some parts but the data race persisted and I just cannot understand why. There are counters inside critical sections for the number of productions and consumptions. I feel completely blind when looking through the code and analyzing it, I'm aware that consumptions are too many but the synchronization around that code fragment should with my knowledge be correct, but obviously something's not right. External input would be greatly appreciated.
This is the code I'm using and I'm not sure how to reduce its size to still reproduce the issue. I compile it with gcc (clang-1205.0.22.11) on macOS Monterey (12.1) using a MacBook Pro 2020 x86_64 architecture.
compile: gcc -o 8q 8q.c*
run: ./8q <consumers> <N>, NxN chess board, N queens to place
parameters: ./8q 2 4 Enough to highlight the problem (should yield 2 solutions, but every other run yields 3+ solutions, i.e duplicate solutions exist
note: running the program with ./8q 2 4 should give 2 solutions, 1820 productions and 1820 consumptions.
#ifndef _REENTRANT
#define _REENTRANT
#endif
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
typedef struct stack_buf {
int positions[8];
int top;
} stack_buf;
typedef struct global_buf {
int positions[8];
int buf_empty;
long prod_done;
int last_done;
} global_buf;
typedef struct print_buf {
int qpositions[100][8];
int top;
} print_buf;
stack_buf queen_comb = { {0}, 0 };
print_buf printouts = { {{0}}, 0 };
global_buf global = { {0}, 1, 0, 0 };
int N; //NxN board and N queens to place
long productions = 0;
long consumptions = 0;
pthread_mutex_t buffer_mutex, print_mutex;
pthread_cond_t empty, filled;
/* ##########################################################################################
################################## VALIDATION FUNCTIONS ##################################
########################################################################################## */
/* Validate that no queens are placed on the same row */
int valid_rows(int qpositions[]) {
int rows[N];
memset(rows, 0, N*sizeof(int));
int row;
for (int i = 0; i < N; i++) {
row = qpositions[i] / N;
if (rows[row] == 0) rows[row] = 1;
else return 0;
}
return 1;
}
/* Validate that no queens are placed in the same column */
int valid_columns(int qpositions[]) {
int columns[N];
memset(columns, 0, N*sizeof(int));
int column;
for (int i = 0; i < N; i++) {
column = qpositions[i] % N;
if (columns[column] == 0) columns[column] = 1;
else return 0;
}
return 1;
}
/* Validate that left and right diagonals aren't used by another queen */
int valid_diagonals(int qpositions[]) {
int left_bottom_diagonals[N];
int right_bottom_diagonals[N];
int row, col, temp_col, temp_row, fill_value, index;
for (int queen = 0; queen < N; queen++) {
row = qpositions[queen] / N;
col = qpositions[queen] % N;
/* position --> left down diagonal endpoint (index) */
fill_value = col < row ? col : row; // closest to bottom or left wall
temp_row = row - fill_value;
temp_col = col - fill_value;
index = temp_row * N + temp_col; // board position
for (int i = 0; i < queen; i++) { // check if interference occurs
if (left_bottom_diagonals[i] == index) return 0;
}
left_bottom_diagonals[queen] = index; // no interference
/* position --> right down diagonal endpoint (index) */
fill_value = (N-1) - col < row ? N - col - 1 : row; // closest to bottom or right wall
temp_row = row - fill_value;
temp_col = col + fill_value;
index = temp_row * N + temp_col; // board position
for (int i = 0; i < queen; i++) { // check if interference occurs
if (right_bottom_diagonals[i] == index) return 0;
}
right_bottom_diagonals[queen] = index; // no interference
}
return 1;
}
/* ##########################################################################################
#################################### HELPER FUNCTION(S) ####################################
########################################################################################## */
/* print the collected solutions */
void print(print_buf printouts) {
static int solution_number = 1;
int placement;
pthread_mutex_lock(&print_mutex);
for (int sol = 0; sol < printouts.top; sol++) { // all solutions
printf("Solution %d: [ ", solution_number++);
for (int pos = 0; pos < N; pos++) {
printf("%d ", printouts.qpositions[sol][pos]+1);
}
printf("]\n");
printf("Placement:\n");
for (int i = 1; i <= N; i++) { // rows
printf("[ ");
placement = printouts.qpositions[sol][N-i];
for (int j = (N-i)*N; j < (N-i)*N+N; j++) { // physical position
if (j == placement) {
printf(" Q ");
} else printf("%2d ", j+1);
}
printf("]\n");
}
printf("\n");
}
pthread_mutex_unlock(&print_mutex);
}
/* ##########################################################################################
#################################### THREAD FUNCTIONS ####################################
########################################################################################## */
/* entry point for each worker (consumer) workers will
check each queen's row, column and diagonal to evaluate
satisfactory placements */
void *eval_positioning(void *id) {
long thr_id = (long)id;
int qpositions[N]; // on stack (thread-private)
while (1) {
pthread_mutex_lock(&buffer_mutex);
while (global.buf_empty == 1) { // no element
if (global.last_done) { // last done, no combinations left
pthread_mutex_unlock(&buffer_mutex);
pthread_cond_signal(&filled);
return NULL;
}
if (global.prod_done) {
global.last_done = 1;
break;
}
pthread_cond_wait(&filled, &buffer_mutex);
}
memcpy(qpositions, global.positions, N*sizeof(int)); // copy to local scope
global.buf_empty = 1;
consumptions++;
pthread_mutex_unlock(&buffer_mutex);
pthread_cond_signal(&empty);
if (valid_rows(qpositions) && valid_columns(qpositions) && valid_diagonals(qpositions)) {
pthread_mutex_lock(&print_mutex);
memcpy(printouts.qpositions[printouts.top], qpositions, N*sizeof(int)); /* save for printing later */
printouts.top++;
pthread_mutex_unlock(&print_mutex);
}
}
return NULL;
}
/* recursively generate all possible queen_combs */
void rec_positions(int pos, int queens) {
if (queens == 0) { // base case
pthread_mutex_lock(&buffer_mutex);
while (global.buf_empty == 0) { // while production hasn't been consumed
pthread_cond_wait(&empty, &buffer_mutex);
}
memcpy(global.positions, queen_comb.positions, N*sizeof(int));
productions++;
global.buf_empty = 0;
pthread_mutex_unlock(&buffer_mutex);
pthread_cond_signal(&filled);
return;
}
for (int i = pos; i <= N*N - queens; i++) {
queen_comb.positions[queen_comb.top++] = i;
rec_positions(i+1, queens-1);
queen_comb.top--;
}
}
/* binomial coefficient | without order, without replacement
8 queens on 8x8 board: 4'426'165'368 queen combinations */
void *generate_positions(void *arg) {
rec_positions(0, N);
pthread_mutex_lock(&buffer_mutex);
global.prod_done = 1;
pthread_mutex_unlock(&buffer_mutex);
pthread_cond_broadcast(&filled); //wake all to
return NULL;
}
/* ##########################################################################################
########################################## MAIN ##########################################
########################################################################################## */
/* main procedure of the program */
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("usage: ./8q <workers> <board width/height>\n");
exit(1);
}
int workers = atoi(argv[1]);
N = atoi(argv[2]);
if (N < 2 || N > 8) {
printf("Wrong input! 2 <= N <= 8\n");
return 0;
}
struct timeval start, stop;
double elapsed;
pthread_t consumers[workers];
pthread_t producer;
printf("\n");
gettimeofday(&start, NULL);
pthread_create(&producer, NULL, generate_positions, NULL);
for (long i = 0; i < workers; i++) {
pthread_create(&consumers[i], NULL, eval_positioning, (void*)i+1);
}
pthread_join(producer, NULL);
for (int i = 0; i < workers; i++) {
pthread_join(consumers[i], NULL);
char id[2];
sprintf(id, "%d", i+1);
write(1, id, strlen(id));
write(1, " done\n\n", 6);
}
gettimeofday(&stop, NULL);
elapsed = stop.tv_sec - start.tv_sec;
elapsed += (stop.tv_usec - start.tv_usec) / (double)1000000;
/* go through all valid solutions and print */
print(printouts);
printf("board: %dx%d, workers: %d (+1), exec time: %fs, solutions: %d\n", N, N, workers, elapsed, printouts.top);
printf("productions: %ld\nconsumptions: %ld\n", productions, consumptions);
return 0;
}
You're not initializing your mutexes and condition variables. The result is UB when used in pthread APIs. Two ways to do this, the simplest is just use the proper initializer:
pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t filled = PTHREAD_COND_INITIALIZER;
Unrelated, but worth mentioning, the last_done ideology is not necessary. This can be done with just the buf_empty and prod_done states. Specifically:
void *eval_positioning(void *tid)
{
int qpositions[N]; // on stack (thread-private)
while (1)
{
pthread_mutex_lock(&buffer_mutex);
// while still producing *and* the buffer is empty
while (!global.prod_done && global.buf_empty)
pthread_cond_wait(&filled, &buffer_mutex);
// if both are true, we're done. nothing to process, and
// there never will be (e.g. prod_done)
if (global.prod_done && global.buf_empty)
{
// signal anyone else waiting on that mutex+cvar
pthread_cond_signal(&filled);
break;
}
// if we have a buffer to process (even if prod_done is true)
else if (!global.buf_empty)
{
// make local copy of buffer
memcpy(qpositions, global.positions, sizeof qpositions);
++consumptions;
// mark global buffer as ready-to-receive
global.buf_empty = 1;
pthread_cond_signal(&empty);
pthread_mutex_unlock(&buffer_mutex);
// if validated...
if (valid_rows(qpositions) && valid_columns(qpositions) && valid_diagonals(qpositions))
{
// record and bump the printout counter.
pthread_mutex_lock(&print_mutex);
int row = printouts.top++;
pthread_mutex_unlock(&print_mutex);
// this need not be protected by the mutex. we "own"
// this now, and can just blast away.
memcpy(printouts.qpositions[row], qpositions, sizeof qpositions);
}
}
else
{
pthread_mutex_unlock(&buffer_mutex);
}
}
// make sure we unlock this
pthread_mutex_unlock(&buffer_mutex);
return tid;
}
With proper initialization of the concurrency materials, and the above eval processor, this is the consistent output:
Output
1 done
2 done
Solution 1: [ 2 8 9 15 ]
Placement:
[ 13 14 Q 16 ]
[ Q 10 11 12 ]
[ 5 6 7 Q ]
[ 1 Q 3 4 ]
Solution 2: [ 3 5 12 14 ]
Placement:
[ 13 Q 15 16 ]
[ 9 10 11 Q ]
[ Q 6 7 8 ]
[ 1 2 Q 4 ]
board: 4x4, workers: 2 (+1), exec time: 0.013001s, solutions: 2
productions: 1820
consumptions: 1820
Apologies for the puny laptop performance numbers
I started C and I am trying to write a tic tac toe with an opponent which searches a random number between 1 to 9 and then fill the slot with “O”. However, when the random number detects an occupied slot, it will continue to fill other empty slots without giving the player the turn. How do I resolve this?
I made two arrays, one for the game screen, one for the memory of the slots.
I’m sorry I couldn’t chuck out the code since I thought it’s all important and is there any better way to do this? Sorry for my writing style if it is a bit confusing, and I think there are unimportant variables.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char values[3][4] = { // screen board
{46, 46, 46,'\n'},
{46, 46, 46,'\n'},
{46, 46, 46,'\n'}
};
double memory[3][4] = { // memory board to put values
{0, 0, 0,'\n'},
{0, 0, 0,'\n'},
{0, 0, 0,'\n'}
};
int player(int i, char values[3][4]) {
int input;
printf("enter position: ");
scanf("%i", &input);
int x = (((input) / 3.3) - 3) * -1; // important math to convert to num
int y = (input + 2) % 3; // important math to convert to num
if (memory[x][y] == 0) {
values[x][y] = 'X';
memory[x][y] = 1;
printf("%s", values);
getchar();
return 0;
getchar();
} else {
printf("Wrong!, choose another line\n");
printf("%s", values);
getchar();
player(i, values);
}
}
int opponent(char values[3][4]) { //function opponent
int count = 0;
srand(time(NULL));
int random = (rand() % 9) + 1; // create random number
for (count = 0; count < 9; count++) {
int x = (((random) / 3.3) - 3) * -1;
int y = (random + 2) % 3;
if (memory[x][y] == 0) { // if memory is empty, do the following, loop stucks here
values[x][y] = 'O';
memory[x][y] = 2;
printf("Opponent Move\n");
printf("%s", values);
count++;
return 0;
} else { // if memory is not 0, do this. Error starts here
getchar();
printf("Move is %i", random);
opponent(values); // it calls itself to do a loop,
}
}
}
int main() {
int input;
int i = 2;;
for (i = 2; i < 9; i++) {
player(i, values); //Player goes first
getchar();
opponent(values);
}
}
Instead of having two independent values and memory, use an enum array to represent the game map.
enum Square { VACANT, X, O } squares[3][3], move = X;
It is initialised according to The initialization of static variables in C.
You probably need a function to decide if a player has won,
/* Checks if the player who's move it was won. */
static int is_win(void) {
return (squares[0][0]==move && squares[0][1]==move && squares[0][2]==move)||
(squares[1][0]==move && squares[1][1]==move && squares[1][2]==move)||
(squares[2][0]==move && squares[2][1]==move && squares[2][2]==move);
/* Etc. */
}
As well as a function for printing the board,
static const char letters[] = { '/', 'X', 'O' };
static void print_board(void) {
printf("%c %c %c\n%c %c %c\n%c %c %c\n",
letters[squares[0][0]], letters[squares[0][1]], letters[squares[0][2]],
letters[squares[1][0]], letters[squares[1][1]], letters[squares[1][2]],
letters[squares[2][0]], letters[squares[2][1]], letters[squares[2][2]]);
}
The code as you have it shadows the global state with parameters to the functions. This is very confusing. Think about whether you need the parameter to do the function's job. When one has complicated states that are defined in multiple files, it's probably best to have a agglomeration game object, but for simple games I think it's fine to have a global state.
Instead of playing until 7 moves, use a simple state machine to keep track of the game state. One can typedef the functions (How do function pointers in C work?) player and opponent and put them in a static array to simplify greatly the game loop. Consider,
/* Move returns whether we should continue. */
typedef int (*Move)(void);
/* Implements Move. */
static int player(void) {
printf("player:\n");
/* FIXME: player move. */
return is_win() ? 0 : (move = O, 1);
}
/* Implements Move. */
static int opponent(void) {
printf("opp:\n");
/* FIXME: Chose randomly from all of it's allowed moves? */
return is_win() ? 0 : (move = X, 1);
}
static const Move states[] = { 0, &player, &opponent };
Then your main is just,
int main(void) {
while(states[move]()) print_board();
printf("%c wins.\n", letters[move]);
return 0;
}
Edit: Definitely have a state where it's a tie, perhaps when there are no moves left.
I'm very new to programming in C, and have pretty rusty programming skills overall. In order to learn C and re-orient myself with programming in general, I'm challenging myself to try and make a simple rougelike using ncurses.
I've set up a "log", which I should be able to push messages to - the most recent 10 message should be displayed. In order to test this, I've made it so each time either the player or the very simple randomly-moving mob takes a step, a log message is pushed saying "step [direction]". However, even though they each only take one step, for some reason, four messages are pushed to the log. The second-to-most-recent one is always the actual direction the character moved, and I presume one of the other two is the mob moving, but I don't know the origin of the other two. Does anyone spot anything glaring in my code that might be causing this issue? All help is appreciated, thanks!
(I believe the only major relevant sections to look at should be the main() function, pushToLog(), printLog(), and moveCreature(). That said, there is a chance the problem might be somewhere else. I'm not sure.)
#include <stdlib.h>
#include <stdio.h>
#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#define up 65
#define down 66
#define right 67
#define left 68
#define quit 113
struct creature {
int x;
int y;
int hp;
int maxhp;
};
void setupMap();
struct creature setupCreature();
void moveCreature();
void pushToLog();
void printLog();
int breakFlag = FALSE;
char mapShape[15][15];
char mapFeatures[15][15];
char outputLog[10][60];
int main(int argc, char *argv[]){
struct creature player = setupCreature(4, 4, 100, 100);
struct creature mob = setupCreature(5, 7, 100, 100);
setupMap();
initscr();
noecho();
curs_set(FALSE);
while(1){
for (int i = 0; i < 15; i++){
for (int c = 0; c < 15; c++){
mvprintw(c, i, "%c", mapShape[i][c]);
}
}
mvprintw(player.y, player.x, "%c", '#');
mvprintw(mob.y, mob.x, "%c", 'd');
printLog();
int input = getch();
moveCreature(input, &player);
int mobDir = rand() % (68 + 1 - 65) + 65;
moveCreature(mobDir, &mob);
refresh();
usleep(300);
if (breakFlag == TRUE){
break;
}
}
endwin();
return 0;
}
void moveCreature(int dir, struct creature *subject){
int next;
if (dir == up){
next = (subject->y - 1);
if (mapShape[subject->x][next] != '#'){
subject->y = next;
pushToLog("step up ");
}
}
else if (dir == down){
next = (subject->y + 1);
if (mapShape[subject->x][next] != '#'){
subject->y = next;
pushToLog("step down ");
}
}
else if (dir == right){
next = (subject->x + 1);
if (mapShape[next][subject->y] != '#'){
subject->x = next;
pushToLog("step right ");
}
}
else if (dir == left){
next = (subject->x - 1);
if (mapShape[next][subject->y] != '#'){
subject->x = next;
pushToLog("step left ");
}
}
else if (dir == quit){
breakFlag = TRUE;
}
}
void pushToLog(char string[]){
for (int i = 10; i > 0; i--){
strcpy(outputLog[i], outputLog[i-1]);
}
strcpy(outputLog[0], string);
}
void printLog(){
for (int i = 0; i < 10; i++){
mvprintw(28-i, 0, outputLog[i]);
}
}
struct creature setupCreature(int x,int y,int hp,int maxhp){
struct creature frankenstien;
frankenstien.x = x;
frankenstien.y = y;
frankenstien.hp = hp;
frankenstien.maxhp = maxhp;
return frankenstien;
}
void setupMap(){
for (int i = 0; i < 15; i++){
for (int c = 0; c < 15; c++){
mapShape[i][c] = '.';
}
}
for (int i = 0; i < 15; i++){
mapShape[0][i] = '#';
mapShape[14][i] = '#';
mapShape[i][0] = '#';
mapShape[i][14] = '#';
}
}
Your problem is at the input stage. You expect directional commands via the arrow keys, but those generate multiple bytes per keypress. All but one are invalid as commands.
As a secondary problem, you do not reject invalid commands. You go ahead and move the mob after each command character read, whether that command was valid or not.
The overall upshot is that when you press an arrow key, the program zips through three iterations of the main loop, one right after the other, producing log messages for one valid player move, no log messages for two invalid player moves, and log messages for each of three mob moves.
You could have detected this by logging invalid commands, or by running your program in a debugger.
Recursion is the one thing I have loads of trouble with. For this assignment we are supposed to print out an outline of sections and sub-sections. Take the following as an example.
Section 1
Section 1.A
Section 1.A.1
Section 1.A.2
Section 1.B
Section 1.B.1
Section 1.B.2
Section 2
Section 2.A
Section 2.A.1
Section 2.A.2
Section 2.B
Section 2.B.1
Section 2.B.2
This example has a depth of 3 and height of 2.
Here is my code thus far, which doesn't come anywhere near the correct output.
void printDepth(int depth, int width, int minusDepth, int minusWidth) {
int i = 0;
if(depth == 0)
printf("Section XX\n");
else {
printf("\t");
printDepth(depth -1, width, minusDepth, minusWidth);
}
}
Here is the call from main()
int minusDepth = 0;
int minusWidth = 0;
printDepth(depth, width, minusDepth, minusWidth);
However, for a depth = 4 and width = 5 this only prints out:
\t\t\t\tSection XX
I'm really not sure how to proceed. Recursion is the bane of my existence.
You need a loop that prints the sections at the current depth and executes height (which I call width instead) recursions. You also need to pass a string containing the current prefix of the section string.
#include <stdio.h>
#include <assert.h>
#include <math.h>
#define MAX_DEPTH 100
#define MAX_WIDTH 99 // NOTE: if you increase this then section's decl in printDepth needs to be updated too
void printDepth_r(int currDepth, int depth, int width, char *section, char *sub)
{
if (currDepth == depth) // recursion base case
return;
for (int i = 0; i < width; ++i)
{
// TODO: write to sub the subsection determined by (currDepth, width)
fprintf(stdout, "%*sSection %s\n", currDepth * 2, "", section);
// TODO: append "." to sub for descendant recursions
printDepth_r(currDepth + 1, depth, width, section, sub + /* TODO: num chars written to sub */);
}
}
int printDepth(int depth, int width)
{
char section[MAX_DEPTH * (2 + 1) + 1]; // NOTE: 2 == 1 + (int) (log(99) / log(10));
assert(sizeof(section) >= MAX_DEPTH * (1 + (int) (log(MAX_WIDTH) / log(10)) + 1) + 1);
if (depth > MAX_DEPTH || width > MAX_WIDTH)
return -1;
printDepth_r(0, depth, width, section, section);
return 0;
}
int main(int argc, char **argv)
{
printDepth(3, 2);
return 0;
}
Note that we pass the same values of depth, width and section to all of our recursions. So, if we wanted to reduce the amount of stack space the recursion eats at every level, then we could pull these out into a structure and pass 1 struct pointer to these 3 constants instead. Or, even better, we could store these values in thread local storage. Either way would allow deeper recursions before overflowing your stack.
#include <stdio.h>
void printDepth(int deep, int depth, int height, char *table) {
if(deep == depth)
return ;
int i, j;
char bak = table[deep];
for(i = 0; i < height; ++i){
printf("%*s%s", deep*2, "", "Section ");
for(j = 0; j <= deep; ++j){
if(j)
putchar('.');
putchar(table[j]);
}
putchar('\n');
printDepth(deep+1, depth, height, table);
table[deep]++;
}
table[deep] = bak;//restore
}
int main(void){
char table[] = "1A1";//Effective range up to 1-9 and A-Z
printDepth(0, 3, 2, table);
return 0;
}
For a JPEG image compression, I manipulate image in grey levels and 8bits by pixels
I have this type of matrix I dynamically allocated :
typedef char pixel_t;
pixel_t ** pix_matrix;
after allocating and filling it, I have a bidimensional array with the values (from -128 to +127) of the luminance of the picture.
For the JPEG compression, I need to iterate this array in zigzag like this:
So I want to create an Iterator structure for this type. This iterator must have 'current' and 'begin' members and I want those members to be pointers to the current element and first one of the matrix. In other words, I want to store the addresses and not the indexes. But after hours of tests, prints and researches, I couldn't find the way to make that possible. What type of pointer do I have to use? how make it point to the first address of my matrix? Is my request simply possible?
And if all of this is possible, how can I get the next element, and the value of the current one?
You can write an interator structure:
struct zigzag_t {
int width; // width, must be initialised
int height; // height, must be initialised
int x; // current x index
int y; // current y index
int underway; // dummy value to start at (0, 0)
};
which you must initialise with the width and height of your image. Write an interator function, so that you can use this iterator like this:
struct zigzag_t zz = {8, 8};
while (zigzag_next(&zz)) {
printf("(%d, %d)\n", zz.y, zz.x);
}
The iterator itself is not too complicated: If the sum of the x and y indices is odd, you walk southwest until you hit either the west or south edge. If the sum is even, you walk northeast until you hit either the north or east wall. If you hit the ne or sw edges, the east and south edges get priority. The iteration ends after you have visited the se edge.
Because the struct starts off with x and y both zero, the first point is (0, 1). In order to fix this, the dummy field underway, which also is zero, is used.
The iterator must be reset if you want to use it a second time. better yet, define and initialise a fresh iterator.
The iterator function:
int zigzag_next(struct zigzag_t *zz)
{
int odd = (zz->x + zz->y) % 2;
if (zz->underway == 0) {
zz->x = zz->y = 0;
zz->underway = 1;
return 1;
}
if (odd) {
/* walk southwest */
int w_edge = zz->x == 0;
int s_edge = zz->y == zz->height - 1;
if (s_edge) {
zz->x++;
return zz->x < zz->width;
} else if (w_edge) {
zz->y++;
} else {
zz->x--;
zz->y++;
}
} else {
/* walk northeast */
int e_edge = zz->x == zz->width - 1;
int n_edge = zz->y == 0;
if (e_edge) {
zz->y++;
return zz->y < zz->height;
} else if (n_edge) {
zz->x++;
} else {
zz->x++;
zz->y--;
}
}
return 1;
}
This solution returns the x and y positions, which you can use as indices to your double pointer to pixel data. It would not be hard to extend the struct to hold the base pointer to your pixel data and have the iterator function return a pointer to a pixel or NULL if the iteration has run out.
An example solution with pointers is below.
#include <stdlib.h>
#include <stdio.h>
typedef char pixel_t;
struct zigzag_t {
pixel_t **p; // base data
int width; // width, must be initialised
int height; // height, must be initialised
int x; // current x index
int y; // current y index
int underway; // dummy value to start at (0, 0)
};
pixel_t *zigzag_next(struct zigzag_t *zz)
{
int odd = (zz->x + zz->y) % 2;
if (zz->underway == 0) {
zz->x = zz->y = 0;
zz->underway = 1;
return *zz->p;
}
if (odd) {
/* walk southwest */
int w_edge = zz->x == 0;
int s_edge = zz->y == zz->height - 1;
if (s_edge) {
zz->x++;
if (zz->x == zz->width) return NULL;
} else if (w_edge) {
zz->y++;
} else {
zz->x--;
zz->y++;
}
} else {
/* walk northeast */
int e_edge = zz->x == zz->width - 1;
int n_edge = zz->y == 0;
if (e_edge) {
zz->y++;
if (zz->y == zz->height) return NULL;
} else if (n_edge) {
zz->x++;
} else {
zz->x++;
zz->y--;
}
}
return zz->p[zz->y] + zz->x;
}
int main()
{
pixel_t *data[] = {
"abcde", "fghij", "klmno", "pqrst", "uvwxy"
};
struct zigzag_t zz = {data, 5, 5};
for (;;) {
pixel_t *p = zigzag_next(&zz);
if (p == NULL) break;
putchar(*p);
}
putchar('\n');
return 0;
}
This solution is a C solution. There is no begin member function; initialisation is done via simple struct initialisation. There is no increment operator and no end member function; moving the iterator forward and checking for the end is done in a plain old function.
You have tagged the question C, but iterators are more frequent in C++, where they can be implemented as classes. The above C example may serve as a base for such an implementation.
Something nice and simple.
Function next is the iterator; it returns true until all cells have been visited.
A variable of type POSITION holds the iterator state.
Function current returns a pointer to the current cell in the matrix.
Demo function sample_application puts it all together.
#define MAX_XY 7
typedef struct { int x, y; } POSITION;
static int sign_of(int i)
{
return i < 0 ? -1 : i > 0 ? 1 : 0;
}
static int get_direction(int a, int b, int odd_is_forward)
{
return sign_of(((a + b) % 2 == odd_is_forward || b >= MAX_XY ? MAX_XY : 0) - a);
}
int next(POSITION *pos)
{
int x = pos->x;
int y = pos->y;
pos->x += get_direction(x, y, 0);
pos->y += get_direction(y, x, 1);
return x < MAX_XY || y < MAX_XY;
}
pixel_t *current(POSITION *pos)
{
return &pix_matrix[pos->y][pos->x];
}
void sample_application() // just demonstrating the use of POSITION
{
POSITION pos = {-1, -1}; // always start from these dummy coordinates
while (next(&pos)) // this iterates through the matrix
{
int coord_x = pos.x; // this is how you get the current coordinates
int coord_y = pos.y;
*current(&pos) = 12; // this is how you access the current cell
}
}