C, ncurses; if statement within while loop not functioning properly - c

I'm writing a function that draws # characters onto a console screen, and repeats itself until it draws upon a 'H' which has already been drawn. Basically a "bomb" function. I wrote the function with a while loop so that it may continue to draw the character as long as the space it is drawing upon does not equal 'H'. If it does equal 'H', i've put in an if statement to break the program. However even with the if statement, the program continues to run when it is drawn on an 'H'.
void bomb_until_hit(int home_radius) {
int x = 0, y = 0;
while (mvinch(y, x) != 'H') {
x = get_next_bomb_x();
y = get_next_bomb_y();
mvaddch(y, x, '#' );
refresh();
sleep(1);
if (mvinch(y, x) == 'H') {
break; }
}
}
mvinch is a function which basically checks the coordinates for characters before the new character is drawn.
Why is my if statement not working?

Your code draws an # and then checks for an H (twice). Obviously, it's not going to find an H since it just drew an #.
Let's look at what happens starting from when you move to a new position:
void bomb_until_hit(int home_radius) {
int x = 0, y = 0;
while (mvinch(y, x) != 'H') { // 5) Check for an H here
x = get_next_bomb_x(); // 1) Move to next X
y = get_next_bomb_y(); // 2) Move to next Y
mvaddch(y, x, '#' ); // 3) Put an # here
refresh();
sleep(1);
if (mvinch(y, x) == 'H') { // 4) Check for an H here
break; }
}
}
You need to rearrange the order of your statements so that once you move to a new spot, you check it before you write to it.

Related

Recursive Flood Fill algorithm in C using ASCII art and incrementing characters to fill the space

I have an assignment where I need to write a recursive flood fill algorithm to fill in spaces in ASCII art with incrementing characters. Now, I have done endless research and think I understand the basic fundamentals of this algorithm. However, when I apply what I learned, the output does not produce what I want. The following is the outline of the program
Find the 'A' within the room where an '*' represents a wall. You can assume there will always be an 'A'.
Start at A and fill the room with incrementing characters up until Z and keep going with Z if Z is reached. Example output:
*****
*DCB*
***A*
*DCB*
*****
Below is a sample program as the original is much bigger.
#include <stdio.h>
#include <stdlib.h>
typedef struct room{
char **array;
int rows;
int cols;
} room;
void printRoom(room room);
int *findA(room room);
void floodFill(room room, int x, int y, char paint);
int main(int argc, char *argv[]){
// Make a new room
room newRoom;
// Set the rows and columns
newRoom.rows = 5;
newRoom.cols = 5;
// Allocate memory for the array
newRoom.array = malloc(newRoom.rows * sizeof(char*));
for(int i = 0; i < newRoom.rows; i++){
newRoom.array[i] = malloc(newRoom.cols * sizeof(char));
}
// Fill the first array with "*****"
for(int i = 0; i < newRoom.cols; i++){
newRoom.array[0][i] = '*';
}
// Fill the second array with "* *"
newRoom.array[1][0] = '*';
newRoom.array[1][1] = ' ';
newRoom.array[1][2] = ' ';
newRoom.array[1][3] = ' ';
newRoom.array[1][4] = '*';
// Fill the third array with "**A*"
newRoom.array[2][0] = '*';
newRoom.array[2][1] = '*';
newRoom.array[2][2] = '*';
newRoom.array[2][3] = 'A';
newRoom.array[2][4] = '*';
// Fill the fourth array with "* *"
newRoom.array[3][0] = '*';
newRoom.array[3][1] = ' ';
newRoom.array[3][2] = ' ';
newRoom.array[3][3] = ' ';
newRoom.array[3][4] = '*';
// Fill the fifth array with "*****"
for(int i = 0; i < newRoom.cols; i++){
newRoom.array[4][i] = '*';
}
printf("Before\n");
printRoom(newRoom);
// Find the A
int *a = findA(newRoom);
// Print the A
printf("A is at %d, %d\n", a[0], a[1]);
// Flood fill the room
floodFill(newRoom, a[0], a[1], 'A');
// Print the room
printf("\nAfter\n");
printRoom(newRoom);
return 0;
}
int *findA(room room){
int *location = malloc(2 * sizeof(int));
for(int i = 0; i < room.rows; i++){
for(int j = 0; j < room.cols; j++){
if(room.array[i][j] == 'A'){
location[0] = i;
location[1] = j;
}
}
}
return location;
}
void floodFill(room room, int x, int y, char paint){
// If the current position is a wall, return
if(room.array[x][y] == '*'){
return;
}
// If the current position is already painted, return
if(room.array[x][y] == paint){
return;
}
if(x < 0 || x >= room.rows || y < 0 || y >= room.cols){
return;
}
// Paint the current position
room.array[x][y] = paint;
// Flood fill the left position
floodFill(room, x, y + 1, paint);
// Flood fill the right position
floodFill(room, x, y - 1, paint);
// Flood fill the top position
floodFill(room, x + 1, y, paint);
// Flood fill the bottom position
floodFill(room, x - 1, y, paint);
}
void printRoom(room room){
for(int i = 0; i < room.rows; i++){
for(int j = 0; j < room.cols; j++){
printf("%c", room.array[i][j]);
}
printf("\n");
}
}
Output
Before
*****
* *
***A*
* *
*****
A is at 2, 3
After
*****
* *
***A*
* *
*****
What I think part of the problem is that in the version above, when the first call to floodFill is made, it checks if the current position is equal to paint. In this case it is and then immediately returns and does nothing. However, if I change this line:
From
floodFill(newRoom, a[0], a[1], 'A');
To
floodFill(newRoom, a[1], a[0], 'A');
Output
*****
* *
***A*
* *
*****
A is at 2, 3
After
*****
* *
***A*
*AAA*
*****
As you can see it somewhat filled out the bottom half but not the top. What I think is happening here is that since A is already there when it tries to go up, it exits as the current position is already A when the room was made. Now I'm not sure what to do as I keep getting stuck. If I try to change the base cases I will get infinite recursive calls or it will not do anything at all. In addition, I'm not sure if I need two char parameters in the floodFill function (char oldPaint, char newPaint) to handle the incrementing of characters. Any help is greatly appreciated!
There are these issues:
The algorithm stops immediately, because it finds that at position x, y the paint character ('A') is already there. To solve this, I would write a wrapper function (floodFillStart) that first replaces that 'A' with a space, and then calls floodFill.
The check whether the x and y coordinate are out of range, should happen before any access to the matrix. So that if statement should come first.
The logic to fill with the next character is missing.
The check room.array[x][y] == paint is not good enough once the character is changing: the algorithm will bump into a character that has the preceding value (where the "flood" came from). This could read room.array[x][y] != ' ', so performing the wall-check at the same time.
void floodFill(room room, int x, int y, char paint){
// First this
if(x < 0 || x >= room.rows || y < 0 || y >= room.cols){
return;
}
// If the current position is not a free spot...
if(room.array[x][y] != ' '){
return;
}
room.array[x][y] = paint;
// Increment the character to paint with
if (paint < 'Z') {
paint++;
}
floodFill(room, x, y + 1, paint);
floodFill(room, x, y - 1, paint);
floodFill(room, x + 1, y, paint);
floodFill(room, x - 1, y, paint);
}
void floodFillStart(room room, int x, int y){
// Temporarily clear the starting spot
if (room.array[x][y] == 'A') {
room.array[x][y] = ' ';
}
floodFill(room, x, y, 'A');
}
In the main program call:
floodFillStart(room, x, y);
The problem is that, at the start, the initial cell already contains an A character, so the function immediately returns. One approach is to clear the initial cell before calling floodFill the first time from main, as follows:
// Flood fill the room
newRoom.array[a[0]][a[1]] = ' ';
floodFill(newRoom, a[0], a[1], 'A');
This produces the following output:
Before
*****
* *
***A*
* *
*****
A is at 2, 3
After
*****
*AAA*
***A*
*AAA*
*****

C programming: live console display of recursive function output (knight's tour)

I'm stuck at my first more complex coding task in C: developing a program that solves the knight's tour (chess, knight has to visit every field on the board only once) and it also should have a live display, showing which move the program is taking in real time.
The solution I came up with doesn't support live display though, as it is iterating through a for loop until the maximum number of moves has been reached, then prints the moves. How could I change this code to create real time output?
The displayBoard function is only printing a chessboard in the console output, with the current position of the knight as well as the field's name (e.g. A-1). Plan is to change this to a stream output for console, after my initial problem is solved.
(In conjunction to this question: how exactly does the function calling as if condition work? I stumbled upon this in the webs but sadly there was no explanation to it.)
int knight(int line, int row, int moveNumber)
{
int newLine, newRow, i;
//char cells[]={'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};
//mark visited cell
board[line][row] = moveNumber;
//for-loop for every possible move
for(i = 0; i < 8; i++)
{
//new position
newLine = line + moves[i][0];
newRow = row + moves[i][1];
//is the move on the board?
if(newLine >= 1 && newLine <= N && newRow >= 1 && newRow <= N)
{
//has the cell been visited yet?
if(!board[newLine][newRow])
{
//recursion until 63th move
if(moveNumber == max_moves || knight(newLine, newRow, moveNumber+1))
{
printf("Move %d: from %c-%d to %c-%d ", moveNumber, cells[row-1], line, cells[newRow-1], newLine);
boardDisplay(newLine, newRow);
return 1;
}
}
}
}
// free last visited cell, if there's no move possible anymore
board[line][row] = 0;
return 0;
}

Drawing simultaneously sine and cosine curves

I'm trying to write a c program that draws sine and cosine curves simultaneously by using '+' for sine and 'x' for cosine and '*' when the values of sine and cosine are equal. Here is the code:
#include<stdio.h>
#include<math.h> /* for sin(x) */
#define M_PI 3.14159265358979323846264338327950288
int main() {
double x;
int s_indent;
int c_indent;
for(x = -180.0; x <=180.0; x+=15.0) {
/* compute value */
s_indent = 10 + 10* sin(x/180 * M_PI);
c_indent = 10 + 10* cos(x/180 * M_PI);
if(c_indent == s_indent){
for(;s_indent;--s_indent) putchar(' ');
printf("*\n");
}
else{
for(; s_indent; --s_indent) putchar(' ');
printf("+\n");
/* plot x at position */
for(; c_indent; --c_indent) putchar(' ');
printf("x\n");
}
}
return 0;
}
but the problem with the code is that it produces the curves line by line. Like here:
And I want to make it on the same line like this one here:
Thoughts?
You can: create an empty line
char line[] = " ";
set the characters at the appropriate places
line[c_indent] = 'x';
line[s_indent] = '+';
and then output this line:
puts(line);
The case of both sine and cos in a single point is left to you as an exercise ;)
In the posted code, each symbol is printed in a separate line at the calculated position, but what you have to do is to determine the order of the symbols and print both in the same line.
Consier using a simple function like
void print_after_n_spaces(int n, const char* str)
{
while (n-- > 0)
putchar(' ');
printf("%s", str);
}
Also, add another branch and calculate the difference between the two positions:
for(int x = -180; x <= 180; x += 15)
{
double angle = x / 180.0 * M_PI;
int s_pos = 10.5 + 10.0 * sin(angle);
int c_pos = 10.5 + 10.0 * cos(angle);
// ^^^^ To "round" the values
if (c_pos > s_pos)
{
print_after_n_spaces(s_pos, "+");
// ^^^ Don't print the newline here
print_after_n_spaces(c_pos - s_pos - 1, "x\n");
// ^^^^^^^^^^^^^^^^^ Difference between the positions
}
else if (c_pos < s_pos)
{
// Here prints first the "x" (the cosine), then the "+"
}
else
{
// Here prints only "*"
}
}
In the else statement, check which one that is larger of s_indent and c_indent.
Copy these variables to two new variables largest and smallest.
Iterate with for loop over smallest to print spaces, then print either + or x depending on which was smallest.
Then iterate from smallest to largest, print spaces, then print either + or x depending on which was largest.
An alternative, more elegant solution is to make a function void printline (int n, char symbol) then call it two times.

Why does my function always return 1?

I'm writing a function that's supposed to check whether there is a winner or not in a game of "noughts and crosses", or "three in a row".
The game works by first drawing out 3 x 3 squares in the command prompt. The user then moves the cursor between the squares, and presses enter to place an "X". The next selected square gets an "O" placed in it, and then an "X" again, and so on. After turn 5 (the first possible turn there could be a winner) the program checks if there is a winner after every turn, and if none is found after 9 turns (when all squares have something in them) the program declares a draw.
However, the function I've written to check for a winner always returns 1 when it's called, which means there is a winner (X more specifically, since thatäs the one that made the last move). Therefore the game ends on turn 5, no matter what the squares contain.
Here is my code:
int control(char p[3][3]) {
int i;
for (i = 0; i <= 3; i++) //Checks if any of the horizontals have 3 of the same markers in a row
if (p[i][1] == p[i][2] && p[i][2] == p[i][3])
return 1;
for (i = 0; i <= 3; i++) //Checks if any of the vertical columns have 3 of the same markers
if (p[1][i] == p[2][i] && p[2][i] == p[3][i])
return 1;
else if (p[1][1] == p[2][2] && p[2][2] == p[3][3]) //Checks if top left, middle and bottom right squares have the same marker
return 1;
else if (p[3][1] == p[2][2] && p[2][2] == p[1][3]) //Checks if the top right, middle and bottom left have the same marker
return 1;
else //If none of the above have the same 3 markers, the game keeps going
return 0;
}
You are passing in a 3x3 array (with indices 0, 1 and 2 in each dimension), but in your loops you are iterating 4 times (indices 0, 1, 2 and 3). Try for (i = 0; i < 3; i++) in your loops instead, as your loops will now check values beyond the boundaries of the p array, and if you are unlucky the contents of that memory causes your control function to return 1.
Finally, I think the final else-block should not be in the loop, but rather outside of it: If both loops have run and you have not returned yet, you can safely return 0, but you wouldn't want to return a 0 prematurely before you have checked all your verticals.
Here is the function with the changes to the indices and the diagonal checks and final else-block fished out of the second for-loop:
int control(char p[3][3]) {
int i;
/* Check horizontals */
for (i = 0; i < 3; i++)
if (p[i][0] == p[i][1] && p[i][1] == p[i][2])
return 1;
/* Check verticals */
for (i = 0; i < 3; i++)
if (p[0][i] == p[1][i] && p[1][i] == p[2][i])
return 1;
/* Check diagonals */
if (p[0][0] == p[1][1] && p[1][1] == p[2][2])
return 1;
if (p[2][0] == p[1][1] && p[1][1] == p[0][2])
return 1;
return 0;
}

Two If statements execute at once, instead of one

I've got a program that takes input from a char Array, using the strtok function to check if the input contains the words "up" or "down". If it contains the word "up", my b value is set false (i.e b = 0) and my c value is set to false as well. If the char array contains the words "down", b is set to false, however my c value is set to true (i.e c= 1).
My problem occurs when the word "up" are contained in the string, as the first if statement executes, and prints the resultant characters on the screen, but somehow the second if also executes printing those characters as well. Any input on this matter would be much appreciated
EDIT: The same problem occurs if I use the word "down", both if statements are executed.
int moveC(int y, int x, int b, int i, int c) {
// int c is a static variable(static int c = FALSE;) defined in the previous function
int j;
int k;
switch (b) //assume b is always false (which it is)
{
case FALSE:
if (c == 0) {
mvprintw(y, x, "^");
refresh();
for (j = 1; j <= i; j++) {
mvprintw(y + j, x, ".");
refresh();
}
break;
}
if (c == 1) //tried using else if, same result
{
mvprintw(y, x, "^");
refresh();
for (j = 1; j <= i; j++) {
mvprintw(y - j, x, ".");
refresh();
}
break;
}
}
return 0;
}
Your code is most likely executing twice. The giveaway is that you tried an if/else. Only one block will ever be executed in one execution of an if statement.

Resources