Pthread data race that drives me crazy - c

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

Related

how to see if there are 1 or 2 poker pairs in a hand in C

I am trying to develop a C program that checks if there are 1 or 2 pairs in a 5 card poker hand.
I am using a 5x3 array where every line is a card (the 3rd column being for the \0 character). Every time I execute the code it always shows the "two pairs" print.
I want to make sure that each letter (i, j, a, b) representing each line is different. Any help?
P.S.: This is for a university/college project, I have only started programming a few months ago from absolute scratch, so any detailed explanations on my mistakes would be very much appreciated :)
#include <stdio.h>
#include <stdlib.h>
char (cards[5][3])=
{
"5S", "6D", "4H", "KD", "5C"
};
int main ()
{
pair (cards[5][3]);
return 0;
}
void pair (char (arg[n][0]))
{
int i,j,a,b;
if (i!=j!=a!=b)
{
if ((arg[i][0]==arg[a][0])&&(arg[b][0]!=arg[j][0]))
{
printf("2 -> pair");
}
if ((arg[i][0]==arg[a][0])&&(arg[b][0]==arg[j][0]));
{
printf("3 -> two pairs");
}
if ((arg[i][0]!=arg[a][0])&&(arg[b][0]!=arg[j][0]))
{
printf("there is no pair");
}
}
else
{
printf("there is no pair");
}
}
The posted code has several issues, both logical and syntactical, some have been pointed out in the comments.
Just to pick one, consider this line
if ((arg[i][0]==arg[a][0])&&(arg[b][0]==arg[j][0]));
{
// This body will never be executed ^
}
I'd suggest to restart from scratch and to proceed in small steps. See, for instance, the following minimal implementation
// Include all the needed header files, not the unneeded ones.
#include <stdio.h>
// Declare the functions prototype before their use, they will be defined after.
int count_pairs(int n, char const cards[][3]);
// Always specify the inner size, ^ when passing a multidimensional array
void show_score(int n_pairs);
int have_the_same_value(char const *a, char const *b);
int main (void)
{
char hand[5][3] = {
// ^^^^^^ You could omit the 5, here
"5S", "6D", "4H", "KD", "5C"
};
int n_pairs = count_pairs(5, hand);
// Always pass the size ^ if there isn't a sentinel value in the array
show_score(n_pairs);
return 0;
}
// This is a simple O(n^2) algorithm. Surely not the best, but it's
// a testable starting point.
int count_pairs(int n, char const cards[][3])
{
// Always initialize the variables.
int count = 0;
// Pick every card...
for (int i = 0; i < n; ++i)
{
// Compare (only once) with all the remaining others.
for (int j = i + 1; j < n; ++j)
{ // ^^^^^
if ( have_the_same_value(cards[i], cards[j]) ) {
++count;
}
}
}
return count;
}
int have_the_same_value(char const *a, char const *b)
{
return a[0] == b[0];
}
// Interpret the result of count_pairs outputting the score
void show_score(int n_pairs)
{
switch (n_pairs)
{
case 1:
printf("one pair.\n");
break;
case 2:
printf("two pairs.\n");
break;
case 3:
printf("three of a kind.\n");
break;
case 4:
printf("full house.\n");
break;
case 6:
printf("four of a kind.\n");
break;
default:
printf("no pairs.\n");
}
}
Note that my count_pairs function counts every possible pair, so if you pass three cards of the same kind, it will return 3 (given AC, AS, AD, all the possible pairs are AC AS, AC AD, AS AD).
How to correctly calculate all the poker ranks is left to the reader.
Major improvements can be made to the pair function to make it slimmer. However, this answers your questions and solves several corner cases:
#include <stdio.h>
#include <stdlib.h>
void pairCheck(char hand[][2])
{
int pairCount = 0;
int tmpCount = 0;
char tmpCard = '0';
char foundPairs[2] = {0};
// Check Hand One
for(int i =0; i < 5; i++)
{
tmpCard = hand[i][0];
for(int j = 0; j < 5; j++)
{
if(tmpCard == hand[j][0] && i != j)
{
tmpCount++;
}
if(tmpCount == 1 && (tmpCard != foundPairs[0] && tmpCard != foundPairs[1]))
{
foundPairs[pairCount] = tmpCard;
pairCount++;
}
tmpCount = 0;
}
}
printf("Pair Count Hand One: %i\r\n",pairCount);
//Reset Variables
foundPairs[0] = 0;
foundPairs[1] = 0;
tmpCard = '0';
pairCount = 0;
// Check Hand One
for(int i =0; i < 5; i++)
{
tmpCard = hand[i][1];
for(int j = 0; j < 5; j++)
{
if(tmpCard == hand[j][1] && i != j)
{
tmpCount++;
}
if(tmpCount == 1 && (tmpCard != foundPairs[0] && tmpCard != foundPairs[1]))
{
foundPairs[pairCount] = tmpCard;
pairCount++;
}
tmpCount = 0;
}
}
printf("Pair Count Hand Two: %i",pairCount);
}
int main ()
{
char cards[5][2] = { {'5','H'},{'6','D'},{'4','H'},{'K','D'},{'5','C'}};
pairCheck(cards);
return 0;
}
This function will treat three, four, or five of a kind as a single pair. If you want a different behavior the change should be easy.

Finding the shortest path (between source and destination) with the least number of edges

I am trying to write a program that finds a minimum-length path between a two vertices in a graph, selecting from among such paths one of those that traverses the fewest edges. I used Dijkstra's algorithm with several modifications (below).
The output supposed to be: 0->3->4, but instead, my program prints 0->4.
Why do I get the wrong output?
#include<stdio.h>
#include<string.h>
#define INFINITY 9999
#define n 5
#define s 0
#define d 4
void Dijkstra(int Graph[n][n], int _n,int _s, int _d);
int main()
{
int Graph[n][n] = {
{0, 6, 5, 1, INFINITY},
{6, 0, 3, INFINITY, INFINITY},
{5, 3, 0, 2, 5},
{1, INFINITY, 2, 0, 6},
{INFINITY, INFINITY, 5, 6, 0}
};
Dijkstra(Graph,n,s,d);
getchar();
return 0;
}
void Dijkstra(int Graph[n][n], int _n,int _s, int _d)
{
int distance[n], parent[n], visited[n], edge[n]={0}, mindistance,
nextnode= _s, i, j,temp[n][n], res[n];
//parent[] stores the predecessor of each node
//edge[] stores the number of edged of every vertex's shortest path
for (i = 0; i < n; i++) //create the temp matrix
for (j = 0; j < n; j++)
if (Graph[i][j] == INFINITY)
temp[i][j] = INFINITY;
else
temp[i][j] = Graph[i][j];
for(i=0;i<n;i++)
{
distance[i] = INFINITY; //initialize distance
parent[i] = _s; //initialize parent
visited[i] = 0;
if (distance[i] > 0 && distance[i] < INFINITY)
edge[i]++;
}
distance[_s] = 0;
visited[_s] = 1;
while (visited[_d] == 0)
{
//nextnode gives the node at minimum distance
for (i = 0; i < n; i++)
{
mindistance = temp[_s][i] + distance[i];
if (distance[i] < mindistance && !visited[i])
{
mindistance = distance[i];
nextnode = i;
}
}
//check if a better path exists through nextnode
visited[nextnode] = 1;
if (nextnode != _d)
for (i = 0; i < n; i++)
if (!visited[i])
{
if (mindistance + Graph[nextnode][i] < distance[i])
{
distance[i] = mindistance + Graph[nextnode][i];
parent[i] = nextnode;
edge[i] = edge[nextnode] + 1;
}
if (mindistance + Graph[nextnode][i] == distance[i])
{
if (edge[i] >= edge[nextnode] + 1)
{
parent[i] = nextnode;
edge[i] = edge[nextnode] + 1;
}
}
}
}
//print the path
for (i = 0; i < n; i++)
res[i] = 0;
i = nextnode;
while (i != _s)
{
res[i] = parent[i];
i = parent[i];
}
printf("%d", _s);
printf("->");
for (i = 0; i < n; i++)
{
if (res[i] != 0)
{
printf("%d", res[i]);
printf("->");
}
}
printf("%d", _d);
}
You have a couple of problems in the loop where you choose the next node to traverse. It is clearly incorrect to set
mindistance = Graph[_s][i] + distance[i];
on each iteration, since you need mindistance to track the minimum observed distance across iterations. Instead, before the loop you should set
mindistance = INFINITY;
While we're looking at this loop, observe also that you ignore the edge count criterion in selecting the next node to traverse. You need to use that criterion here, too, to ensure that you find a path that meets your criteria.
With those corrections, your program produces the expected output for me.
Do note, by the way, that this is still pretty straight-up Dijkstra. The trick is to recognize that you're implementing a two-component distance measure. The most-significant component is the path length (sum of edge weights), but the edge count is a secondary component by which ties are broken. The implementation therefore looks a little different, but if you factored out the distance comparison and setting to separate functions then you wouldn't be able to tell the remainder apart from standard Dijkstra (simple variation).
I have edited my code, but I still get the same wrong output:
#include<stdio.h>
#include<conio.h>
#define INFINITY 9999
#define n 5
#define s 0
#define d 4
void Dijkstra(int Graph[n][n], int _n,int _s, int _d);
int main()
{
int Graph[n][n]={{0,6,5,1,INFINITY},{6,0,3,INFINITY,INFINITY},{5,3,0,2,5},{1,INFINITY,2,0,6},{INFINITY,INFINITY,5,6,0}};
Dijkstra(Graph,n,s,d);
getchar();
return 0;
}
void Dijkstra(int Graph[n][n],int _n,int _s,int _d)
{
int temp[n][n],distance[n],parent[n],visited[n],mindistance,nextnode,j,i;
//parent[] stores the predecessor of each node
//edge[] stores the number of edges in the shortest path from the source
//create the cost matrix
for(i=0;i<_n;i++)
for(j=0;j<_n;j++)
temp[i][j]=Graph[i][j];
//initialize
for(i=0;i<_n;i++)
{
distance[i]=INFINITY;
parent[i]=_s;
visited[i]=0;
}
distance[_s]=0;
visited[_s]=1;
nextnode=_s;
while(visited[_d]==0)
{
mindistance=INFINITY;
//nextnode gives the node at minimum distance
for(i=0;i<_n;i++)
{
distance[i]=temp[nextnode][i];
if(distance[i]<mindistance && !visited[i])
{
mindistance=distance[i];
nextnode=i;
}
}
//check if a better path exists through nextnode
visited[nextnode]=1;
for(i=0;i<_n;i++)
if(!visited[i])
if(mindistance+temp[nextnode][i]<distance[i])
{
temp[nextnode][i]=mindistance+distance[i];
parent[i]=nextnode;
}
}
//print the path and distance of each node
i=_d;
printf("%d",i);
do
{
i=parent[i];
printf("<-%d",i);
}while(i!=_s);
}
Just a few small changes are needed to get the supposed output; I commented on the code below:
…
distance[_s]=0;
// visited[_s]=1; do not mark the initial node as visited - will select below
// nextnode=_s; no need here - will select as the node at minimum distance
while (!visited[_d])
{
mindistance=INFINITY;
//nextnode gives the node at minimum distance
for (i=0; i<_n; i++)
{
// distance[i]=temp[nextnode][i]; don't change tentative distance here
if (distance[i]<mindistance && !visited[i])
{
mindistance=distance[i];
nextnode=i;
}
}
//check if a better path exists through nextnode
visited[nextnode]=1;
for (i=0; i<_n; i++)
if (!visited[i])
if (mindistance+temp[nextnode][i]<distance[i])
{
// temp[nextnode][i]=mindistance+distance[i]; other way round
distance[i]=mindistance+temp[nextnode][i]; // smaller
parent[i]=nextnode;
}
}
…

Trouble implementing BSP tree for roguelike dungeon generation

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;
}

User defined values aren't honored in program at later points

In the code below, the user is supposed to be able to define what nurses won't be available for work that week. The user has a list of the names, and they are supposed to type a number that corresponds to the name. Once that value is stored into the slackers[4] array, it should be using those user supplied values to remove those nurses from being selected come making selections. It doesn't seem to be honoring those selected values, despite avail_nurses[9] having the correct values at any point in time (I tested using printf statements).
Everything seems to be in good shape except that essential piece of the puzzle. I would appreciate some constructive critique and useful suggestions. If you can avoid it, don't write the code for me--I gotta learn somehow. Thanks in advance!
#include "stdafx.h"
#include <stdlib.h>
#include <time.h>
char *names[] = { "Denise", "Inja", "Jane", "Karen", "Maggie", "Margaret", "MJ", "Queen", "Sherri", NULL }; //ptr for names, 9 nurses
/*0 = Denise, 1 = Inja, 2 = Jane, 3 = Karen, 4 = Maggie, 5 = Margaret, 6 = MJ, 7 = Queen, 8 = Sherri*/
const char days[5][10] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };
int randomNurse();
#define total_nurses 9 //number of nurses on staff
#define days_in_week 5 //number of work days in a week
int main() {
srand(time(NULL));
int day, pos, rand_num, i, j;
int slackers[4] = { 0, 0, 0, 0 }; //array that holds the selections for who isn't working
int avail_nurses[total_nurses] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }; //holds the status of each nurse, 0 = unavailable, 1 = available
/*this allows the user to repeat the program easily! flag determines if we run the program multiple times*/
while (char flag = 'y') {
/*prints names */
int temp_counter = 1; //counter
char **name_ptr = names;
while (*name_ptr) {
printf("%i) %s\n", temp_counter, *name_ptr);
name_ptr++;
temp_counter++;
}
/*this assumes that no more than FOUR nurses will be away on any given week*/
printf("\nEnter numbers that correspond to the nurses who won't be available for the week.\nType up to four numbers, each separated by a space.\n");
printf("When you are done, press \"Enter\".\n");
printf("If less than four nurses will be on leave, type a \"0\" in place of a selection.\n");
printf("Example: 1 2 5 0\n\n\n");
/*week selection of unavailable nurses*/
do {
printf("Who won't be here? ");
} while (scanf("%i %i %i %i", &slackers[0], &slackers[1], &slackers[2], &slackers[3]) != 4);
/*checks the selections made, and sets the available nurses to the correct value, zero if they are slacking||vacationing*/
for (int n = 0; n < 4; n++) {
int slacker = slackers[n];
if (slacker >= 1 && slacker <= 9)
avail_nurses[slacker - 1] = -1;
}
/*-----WEEKLY_ASSIGNMENT-----*/
int pos_per_day[days_in_week] = { 5, 9, 9, 8, 5 }; //number of nurses needed each day
int selection[days_in_week][total_nurses]; //the selected nurses per day
for (i = 0; i < days_in_week; i++) {
for (j = 0; j < total_nurses; j++) {
selection[i][j] = -1; //initialize to -1 which means no nurse is selected
}
}
//fill all the days of week
for (day = 0; day < days_in_week; day++) {
for (pos = 0; pos < pos_per_day[day]; pos++) { //for every position needed that day
do {
rand_num = randomNurse();
} while (!avail_nurses[rand_num]); //looks for available nurses (phrasing)
avail_nurses[rand_num] = 0; //change nurses status to not available
selection[day][pos] = rand_num; //fill the output array with appropriate nurse
}
for (i = 0; i < total_nurses; i++) {
avail_nurses[i] = 1; //initialize the nurses status for next day use
}
for (int n = 0; n < 4; n++) { //make sure we shame the slackers...
int slacker = slackers[n];
if (slacker >= 1 && slacker <= 9)
avail_nurses[slacker - 1] = -1;
}
/*DEBUGGING PRINTFs
printf("\n\nSELECTION:\n");
for (int x = 0; x < days_in_week; x++) {
for (int y = 0; y < total_nurses; y++) {
printf("%i\t", selection[x][y]);
}
printf("\n");
}*/
}
printf("\n");
/*-----PRINTS SCHEDULE FOR WEEK-----*/
for (i = 0; i < days_in_week; i++) {
printf("%-10s: ", days[i]);
for (j = 0; j < total_nurses; j++) {
if (selection[i][j] != -1)
printf("%-10s ", names[selection[i][j]]);
}
printf("\n");
}
fflush(stdin);
/*asks user if they want the program to run again*/
printf("\n\nDo you want to run the program again? (y/n) ");
scanf("%c", &flag);
if (flag == 'n' || flag == 'N') {
printf("\n");
break;
}
else {
printf("\n\n\n");
continue;
}
}
return 0;
}
/*function to generate random nurse*/
int randomNurse() {
return rand() % 9; //random number 0-8, to pick nurse
}
Your avail_nurses array uses the value 1 to indicate a nurse that's available, the value 0 to indicate a nurse that's not available as that nurse has already been assigned, and -1 to indicate a nurse that's not available because that nurse is a slacker. You then test whether or not a nurse is available with !avail_nurses[rand_num]. This statement is true if avail_nurses[rand_num] is 0 and is false if it's any other value. The means when avail_nurses[rand_num] is -1 it will exit the loop just like it does when it's 1.
To fix this bug either change the test to avail_nurses[rand_num] <= 0 or use only 0 to indicate unavailable nurses regardless of the reason why.

Switch-Case statement seems to freeze my loops

Here are a few loops from my program I'm working on. The program seems to stop advancing after printf("TEST2");. Everything checks out at a glance. Is there something I'm missing?
I'm expecting the loop to repeat after setting the values in the switch-statement. I know it's getting through it at least once.
#include "stdafx.h"
#include <stdlib.h>
#include <time.h>
char *names[] = { "Denise", "Inja", "Jane", "Karen", "Maggie", "Margaret", "MJ", "Queen", "Sherri", NULL }; //ptr for names, 9 nurses
const char days[5][10] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };
int randomNurse();
#define total_nurses 9 //number of nurses on staff
#define days_in_week 5 //number of work days in a week
int main() {
srand(time(NULL));
int day, pos, candidate, i, j;
int slackers[4] = { 1, 1, 1, 1 }; //array that holds the selections for who isn't working
char **name_ptr = names;
/*0 = Denise, 1 = Inja, 2 = Jane, 3 = Karen, 4 = Maggie, 5 = Margaret, 6 = MJ, 7 = Queen, 8 = Sherri*/
int avail_nurses[total_nurses] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }; //holds the status of each nurse, 0 = unavailable, 1 = available
/*prints names */
int temp_counter = 1; //counter
while (*name_ptr) {
printf("%i) %s\n", temp_counter, *name_ptr);
name_ptr++;
temp_counter++;
}
/*this assumes that no more than FOUR nurses will be away on any given week*/
printf("\nEnter numbers that correspond to the nurses who won't be available for the week.\nType up to four numbers, each separated by a space.\n");
printf("When you are done, press \"Enter\".\n");
printf("If less than four nurses will be on leave, type a \"0\" in place of a selection.\n");
printf("Example: 1 2 5 0\n\n\n");
/*week selection of unavailable nurses*/
do {
printf("Who won't be here? ");
} while (scanf("%i %i %i %i", &slackers[0], &slackers[1], &slackers[2], &slackers[3]) != 4);
/*checks the selections made, and sets the available nurses to the correct value, zero if they are slacking||vacationing*/
for (int n = 0; n < 4; n++) {
int slacker = slackers[n];
if (slacker >= 1 && slacker <= 9)
avail_nurses[slacker] = -1;
}
/*-----WEEKLY_ASSIGNMENT-----*/
int pos_per_day[days_in_week] = { 5, 9, 9, 8, 5 }; //number of nurses needed each day
int selection[days_in_week][total_nurses]; //the selected nurses per day
for (i = 0; i < days_in_week; i++) {
for (j = 0; j < total_nurses; j++) {
selection[i][j] = -1; //initialize to -1 which means no nurse is selected
}
}
//fill all the days of week
for (day = 0; day < days_in_week; day++) {
for (pos = 0; pos < pos_per_day[day]; pos++) { //for every position needed that day
do {
candidate = randomNurse();
} while (!avail_nurses[candidate]); //looks for available nurses (phrasing)
avail_nurses[candidate] = 0; //change nurses status to not available
selection[day][pos] = candidate; //fill the output array with appropriate nurse
}
for (i = 0; i < total_nurses; i++) {
avail_nurses[i] = 1; //initialize the nurses status for next day use
}
for (int n = 0; n < 4; n++) { //make sure we shame the slackers...
int slacker = slackers[n];
if (slacker >= 1 && slacker <= 9)
avail_nurses[slacker] = -1;
}
}
/*-----PRINTS SCHEDULE FOR WEEK-----*/
for (i = 0; i < days_in_week; i++) {
printf("%-10s: ", days[i]);
for (j = 0; j < total_nurses; j++) {
if (selection[i][j] != -1)
printf("%-10s ", names[selection[i][j]]);
}
printf("\n");
}
return 0;
}
/*function to generate random nurse*/
int randomNurse() {
return rand() % 9; //random number 0-8, to pick nurse
}
You have Undefined Behaviour. The second value in pos_per_day is 9, which is outside the bounds of the select array. Subtracting one from each value in that array may be enough to fix it.
Other bad problems:
you need to validate the input data after scanf.
the switch statement is completely unnecessary. Replace it by a calculation.
don't use UPPER CASE for variables. By convention, that's only for defined constants.
don't hard code numbers like 5 and 9. Replace them by DEFINED CONSTANTS.
You must learn how to debug simple programs like this, using the debugger available to you.

Resources