So I'm trying to display a game-board on my terminal and have players type in their next move (in my case 4-in-a-row). Each time the player makes their input, I'm drawing the board again.
Unfortunately, when inserting those walls and game pieces with each character individually, you can basically see how my slow computer displays each character one by one.
To simplify things here, I'll pretend that I only want to display a large list of characters. I already have most of the logic and inputs working, so I'm just looking for optimizations.
So this was my first approach. Just go through the loop and print it one by one.
// Rows
for(int x = 0; x < 10; x++) {
// Columns
for(int y = 0; y < 10; y++) {
// Note: to keep evererything neatly in-line, I'll just use if-statements
if((y % 3) == 0) printf(" ");
if((y % 3) == 1) printf("X");
if((y % 3) == 2) printf("O");
}
printf("\n"); // Add line-break at the end
}
So my guess is that each time I'm calling printf, the computer analyzes and edits the string accordingly before printing it on my screen. Which probably is the reason why it's so slow. (please correct me if I'm wrong).
As a second approach, I tried to save all the letters inside a char array before printing it.
char text[500]; // Note: In my program I don't know how long the text is.
int index = 0;
// Rows
for(int x = 0; x < 10; x++) {
// Columns
for(int y = 0; y < 10; y++) {
if((y % 3) == 0) text[index] = ' '; // Set text index accordingly
if((y % 3) == 1) text[index] = 'X';
if((y % 3) == 2) text[index] = 'O';
index++; // Increase index
}
text[index] = '\n'; // Add line-break
index++;
}
text[index] = '\0'; // End of string
printf(text);
Though a little overwhelming at first, this code works perfectally for me and actually manages to print the string a lot, lot faster.
The problem with this is that (1) you quickly lose track of your index (so debugging is a real pain) and that (2) you have to live with the fear that your text will be longer than 500 characters (or that you only needed 100 and wasted a lot of free-space).
My question now is: Are there any fast and more consistent ways to display information on your terminal screen? Instead of displaying the game-board each round, could I also just delete the last few characters until I get to the piece that needs to be edited? Could I also freeze the screen until the content is ready?
For those interested, here's (almost) my full C-Code: http://pastebin.com/Emffgsdd
Yes, instead of redrawing everything, everytime, you could only draw the changes (like new play elements on the game). The challenge is keeping track of the screen state.
One well known terminal library that takes care of most of those issues is NCURSES. There is a good online book by Eric Raymond.
Related
I am making a Bingo game in C for a project and have made it through the initial board creation:
B = 1-20, I = 21-40, N = 41-60, G = 61-80, O = 81-99.
I have also been able to randomly generate a letter/number pair that doesn't repeat that is to be used to play the game per the following:
int main()
{
srand(time(0));
int rollcount = 0, rollc, rollr, nohund, basec=5, base=20;
const char BINGO[5] = {'B','I','N','G','O'};
bool check[101];
//fills check array with falses that will change to true as numbers are picked.
for(int i = 0; I < 100; i++)
check[i]=false;
//marks free space as true, free = 100 on board
check[100]=true;
do
{
//test var to prevent duplicate "rolls" until a game is won
bool reroll=true;
rollcount++;
//here's where the problem starts
//This is meant to loop if the number rolled has been rolled before.
do
{
//getchar();
if(getchar() == 'q')
{
//future code to display stats and escape game
return 0;
}
rollc = rand() %basec; //pick random column
//pick random number in range of selected column
rollr = rand() %base + 1;
rollr += (rollc * 20);
//limits last col to 99 instead of 100
nohund = (rollr == 100 ? rollr -= 1 : rollr);
if (check[rollr] == false) //checks if number has been used
{
//prints the number of the roll, column letter, and random number
printf("%d: %c-%d", rollcount, BINGO[rollc], rollr);
check[rollr]=true; //prevents future use of that number
reroll=false; //escapes loop to roll another number
}
}
//roll current roll again when duplicate is generated
while(reroll==true);
}
//intended to roll with each press of 'ENTER'
while(getchar()=='\n');
}
I have tried a few things. As written above, the major problem is if a duplicate is found, it shows the blank line from the getchar() input in the if() statement. As more numbers are found, wider and wider gaps between lines appear (due to needing more ENTER presses to find a unique number), leaving a mostly blank screen. With a single ENTER press, I need it to loop until it finds a good number. If I get rid of all getchar() expressions, the program will do this, and I can press ENTER until I get 99 unique numbers, but I need to allow to press q to quit the program.
Example Output
1: B-15
2: G-78
3: I-37
4: G-62
To summarize, I need it to display numbers like it does without getchar(), make it through all 99 possibilities, and let me quit with q.
Please help, and thank you in advance.
Let's first apply some fixes to other parts of the code, to make sure it all works as intended. This is the typical signature for a main function without parameters:
int main(void)
The middle I here needs to be lowercase, otherwise the code won't compile or it will use the imaginary number from the complex numbers:
// Note: index 0 is not used
for(int i = 1; i < 100; i++)
The most direct way of making the program work, would be to move the getchar() == 'q' check to before the do-loop. The function of the do-loop is now to loop until it finds a number which hasn't been chosen before. The problem is now that this will loop forever if all numbers are taken. Therefore, we'll add an additional check: only loop if there are still numbers available.
if(getchar() == 'q')
{
return 0;
}
bool aNumberIsAvailable;
do
{
// ... same code as before ...
aNumberIsAvailable = false;
for(int i = 0; i < 100; i++)
{
if (!check[i])
{
aNumberIsAvailable = true;
break;
}
}
}
while(reroll==true && aNumberIsAvailable);
That said, there are better ways to design this program. One simple step is to combine the two getchars into one, as if it is a menu: A '\n' result means "roll another number" and a 'q' result means "quit". As user3386109 suggested, there are better ways to solve the "sample without replacement"-problem.
Finally, note that getchar does not detect key presses. It simply reads a single character from the (terminal) input buffer. If you would like see actual key up/down movements, you will need a library which gives you more direct access to the keyboard such as SDL2.
can someone explain me why if i press s button or d button it throws me segmetation fault but with a button and w button it works fine.it is just a function to move player p in a square just like snake.
void move(){
int i;
int j;
char choice;
scanf("%s",&choice);
for (i = 0; i < rows; ++i) {
for (j = 0; j < cols; ++j) {
if(maze[i][j] == 'p' && choice == 'a') {
char tmp = maze[i][j];
maze[i][j] = maze[i][j-1];
maze[i][j-1] = tmp;
}
if(maze[i][j] == 'p' && choice == 'w'){
char tmp = maze[i][j];
maze[i][j] = maze[i-1][j];
maze[i-1][j] = tmp;
}
if(maze[i][j] == 'p' && choice == 'd') {
char tmp = maze[i][j];
maze[i][j] = maze[i][j+1];
maze[i][j+1] = tmp;
}
if(maze[i][j] == 'p' && choice == 's') {
char tmp = maze[i][j];
maze[i][j] = maze[i+1][j];
maze[i+1][j] = tmp;
}
}
}
}
Welcome to Stack Overflow, and welcome to programming!
Per related comments, the segfault is most likely due to overrunning the bounds of your maze[][] array. You ask in a couple of your comments how you can deal with this situation.
In the interest of providing the most constructive advice, instead of giving you code, I'm going to ask a question:
How, in programming, do we cause something to happen or not happen under specific conditions?
So, describe for yourself the conditions under which you should not be making references such as i + 1 or j - 1, and then add corresponding logic to your code.
I also encourage you to ask yourself:
How many times does my move() function alter the maze[][] for a single input?
Write down your answer, and then add output statements, or for example a show() function that prints out the total current state of maze[][], in the inner-most part of your loops in move(). See if your expected answer matches your actual output.
Regarding your description of this program that when you :
"press s button or d button it throws me segmentation fault but with a button and w button it works fine"
... I would say that it only appears to be working when you press the a button or the w button. In fact, whenever you reference outside the bounds of an array (which might happen no matter which of w, a, s, or d is pressed), the behavior is undefined. So, by chance, the way the program gets compiled (for you, at a particular time) might not segfault right away when pressing w. When you access outside the bounds of an array, you might access memory that is okay to access, and it will seem to work; but you might access memory that is off limits to your process, and it will segfault.
Bugs related to undefined behavior can be the most subtle to deal with. Logic to serve as guardrails, and debug output statements to show actual value changes can help quite a bit.
Just off the bat this code is incorrect
char choice;
scanf("%s",&choice);
Choice is one character - how can it hold a string of multiple characters?
This would be better
char choice[101];
scanf("%100s", choice);
Would enable a string of up to 100 characters
The 101 - nothing related to George Orwell - enables the null character to be added
You should also check the return value from scanf. Please read the manual page for scanf
BTW - I have not got to the read the rest of the code
I am probably not going to get help here because my question is far from being specific (I don't even know what exactly wrong with it) but, according to my professor's tests, there is something wrong with it (wrong in terms of correctness - it doesn't provide correct number of direct and indirect matches) (I have no access to his tests). As far as I have been testing, it passes all of my tests. However, there are over a couple hundred million possible outcomes (I think) and I can't test them all because I don't know how to do automated testing...
Here is my code that performs the "logic" part of the game called mastermind, which is compares a string of randomly generated letter (8 max) with user input string (a guess). I wanted to see if anyone has encountered this game in the past and knows the logic of how it supposed to compare two strings and generate the correct number of exact and inexact guesses.
// userInput->position - a length of a string(max 8)
// userInput->code - randomly generated code
// userInput->arr - user input string
void checkForExactMatch(Data* userInput) {
int i;
for (i = 0; i < userInput->position; i++) {
if (userInput->code[i] == userInput->arr[i]) {
userInput->exactMatch++;
userInput->arr[i] = 'a';
}
else
checkForInExactMatch(userInput, i);
}
}
void checkForInExactMatch(Data* userInput, int i) {
int j;
for (j = 0; j < userInput->position; j++) {
if (userInput->arr[j] == userInput->code[i]) {
userInput->arr[j] = 'a';
userInput->inExactMatch++;
break;
}
}
}
Looking over your code there were a couple of observations to be made. First in your for checkForExactMatch() the call to checkForInExactMatch is inside your for loop. So on the first mismatch you call checkForInExactMatch and when you return from checkForInExactMatch -- you call it again on the next iteration unless your first mismatch just happens to be on the final character.
To address that issue, you should fully determine whether you have an exact match or not, completing the for loop before checkForInExactMatch is called.
In your checkForInExactMatch, you have to decide whether a single common-character or some minimum length substring constitutes an inexact match.
It sounds like you have things worked out, and good job for pushing through to a solution. Depending on how you approached it, keeping a simple flag in checkForExactMatch()such as int matched = 1; and then loop turning your test around
for (i = 0; i < userInput->position; i++)
if (userInput->code[i] != userInput->arr[i]) {
matched = 0;
break;
}
Then it's just a simple test of
if (matched) {
userInput->exactMatch++;
userInput->arr[i] = 'a';
}
else
checkForInExactMatch(userInput, i);
So long as what you have done accomplishes something similar, you are fine. Let me know if you have further questions.
I am making a little game using getch and print, as a bit of a test for myself, and surprisingly it actually works quite well but i'm having a bit of an issue. I am printing all of the tiles using a loop and printf, and of course as the loop has to process a bit, everytime the character moves, it re-prints everything, which causes a bit of a stutter, due to the loop printing. What can I possibly do to combat this?
This is how the tiles are printed
void Game() {
int X = 0;
int Y = 0;
int PrintWall = 0;
do {
for (int i = 0; i < 80; i++) {
X = i;
PrintWall = 0;
for (int j = 0; j < 12; j++) {
if (X == WallX[j]) {
if (Y == WallY[j]) {
PrintWall = 1;
}
}
}
if (X == Player.XCoor && Y == NegativeToPositive(Player.YCoor)) {
printf("#");
}
else if (PrintWall == 1){
printf("#");
}
else {
printf(".");
}
}
Y++;
} while (Y != 22);
}
You may want to try building the full screen of data into a buffer first and THEN print it. Start with an array of characters as long as you need it to be:
char buffer[SIZE_X * SIZE_Y];
Then go through your loop above, but instead of using printf(), set the character at that location in the buffer to what you want it to be:
buffer[(SIZE_X * Y) + X] = /* '#', '#', or '.' as appropriate */
Then, once you are through the loop, you will print the whole buffer to the screen:
printf(buffer);
Just make sure you don't accidentally overwrite your null terminator with another character or you may get more text than you bargained for.
That is just a limitation of printing the whole "board" on every change. The only way to fix it is to only print what changes. Depending on your OS etc you may be able to print individual characters or lines instead of the whole board.
You could look at a library like curses to give character level control (there may be something better - don't know, I don't do this often enough to know ;-)
As other answers have said, printing in bigger pieces (like a whole buffer at a time) is better than printf for each individual character but you will still probably have the same issue but it is certainly worth a try.
Consider separating initialization and the game loop. Draw the game board as init and update everything in your do-while-loop.
You should limit the number of moving walls.
If a wall moves, you delete/replace the wall mark # at the old position, write out the mark on your interface at the new place and then change the mark's data in the arrays.
After that, you just check, if the new player position hits an obstacle. If no, delete/replace the current mark of the player with the default character and write the new position.
If the two players hit each other, you would need your old condition.
Having a strange problem. I finally figured out how to turn a variable "i" that increments inside a loop into a string that I can pass to a function which turns it into a character that is output to a display.
The trouble is, the value increments by 2 rather than by 1! I have tried a few different things but I'm not sure where the problem lies.
Initially, I thought that perhaps, if you have a function that calls another function, and they both use the same variable to increment (i.e. for(int i=0; i<10; ++i)), then the "outer" function would have "i" increment by two because it is incremented once in the "outer" loop and once in the "inner" loop. However I think this is not the case. If it were so "i" would get incremented by more than two in my case, and I tried changing all the counter variables to different names with no change. It would be a silly way for the language to work, anyway. Unless of course it IS the case, I'd love to be enlightened.
Here is the code block giving me trouble:
for (int i=0; i<100; i++){
char c[1]={0}; // Create variable to hold character
sprintf(c,"%d", i); // Copy value of "i" as string to variable
writeText(c,0,0,WHITE,BLACK,3); // Write the character "c" at position 0,0. Size 3
OLED_buffer(); // Send display buffer
delay_ms(500); // Delay before next increment
}
Here is writeText():
void writeText(unsigned char *string, int16_t x, int16_t y, uint16_t color, uint16_t bgcolor, uint8_t size){
unsigned char letter;
for (int i=0; i<strlen(string); ++i){
letter = string[i];
if (letter != NULL){
drawChar(x+(i*6*size),y,letter,color,bgcolor,size);
}
}
}
Here is drawChar, called by writeText:
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size) {
if((x >= _width) || // Clip right
(y >= _height) || // Clip bottom
((x + 5 * size - 1) < 0) || // Clip left
((y + 8 * size - 1) < 0)) // Clip top
return;
for (int8_t i=0; i<6; i++ ) {
uint8_t line;
if (i == 5)
line = 0x0;
else
line = font[(c*5)+i];
for (int8_t j = 0; j<8; j++) {
if (line & 0x1) {
if (size == 1) // default size
drawPixel(x+i, y+j, color);
else { // big size
fillRect(x+(i*size), y+(j*size), size, size, color);
}
} else if (bg != color) {
if (size == 1) // default size
drawPixel(x+i, y+j, bg);
else { // big size
fillRect(x+i*size, y+j*size, size, size, bg);
}
}
line >>= 1;
}
}
}
And finally drawPixel, called by drawChar (though I sincerely doubt the problem goes this deep):
void drawPixel(int16_t x, int16_t y, uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
return;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 1:
swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
swap(x, y);
y = HEIGHT - y - 1;
break;
}
// x is which column
if (color == WHITE)
buffer[x+(y/8)*SSD1306_LCDWIDTH] |= _BV((y%8));
else
buffer[x+(y/8)*SSD1306_LCDWIDTH] &= ~_BV((y%8));
}
The result of all this is that the display shows a number that increments by twice the length of my delay. So for instance the delay here is 500ms, so it updates every 1 second. Rather than going
1, 2, 3, 4, 5...
as it should, it goes
1, 3, 5, 7, 9...
Does anyone have any advice to offer? I'm sure that it is some stupid simple problem in my initial loop, but I just can't see it right now.
I am using Atmel Studio 6 to program an xmega32a4u. The library functions shown are part of the Adafruit graphics library for the SSD1306 128x32 OLED that I ported into Atmel Studio.
Thanks so much for the help!
UPDATE: Although my code did have some problems, the real issue was actually with the way the OLED was addressed. Apparently Adafruit forgot to set the correct page address in their libraries for the display. As the controller on the display can support a 128x64 as well as a 128x32 display, the "end" address for the display must be set correctly so that the controller knows which parts of the display RAM to access. That function was missing. Because of how the display writes the data ram and because it didn't "know" that the display was only 32 pixels tall, every other frame sent to the display was actually being written to the "bottom" part of the display ram (i.e. the part that WOULD appear if the display was 128x64, twice as tall). So now everything works great!
A big thanks to unwind, if not for his suggestion about the display timing getting me thinking about that side of the issue, it might have taken me a long time to figure out the problem.
You have a buffer overrun.
This:
char c[1]={0}; // Create variable to hold character
sprintf(c,"%d", i);
is not allocating enough room in the string buffer c to hold a single-digit string. Remember that strings in C are 0-terminated, so a 1-digit string requires 2 characters. Since your loop goes to 100, you will eventually write 3 + 1 characters to the buffer, overwriting even more. Not sure how you imagined this to work.
It's likely that sprintf() is overwriting your loop index variable, although anything could happen since you're hitting undefined behavior.
Change those two lines to:
char c[8];
sprintf(c, "%d, i);
or, if you have it, use snprintf():
snprintf(c, sizeof c, "%d", i);
to get protection against buffer overrun.
If you just want the least significant digit of i, do something like this:
snprintf(c, sizeof c, "%d", i % 10);
This uses the modulo (% in C) operator to compute the remainder when dividing by 10, which is the "ones" digit.
UPDATE After reading your comments, I'm inclined to believe that your problem is one of timing, maybe the display contents aren't refreshed when you expect them to be, so that you only see every second "frame" you build. You should be able to use a debugger to pretty easily see that you do indeed build and display each numeric value, by breaking after the sprintf() in the root loop.
UPDATE 2: Just since it bothered me, your writeText() function can be simplified quite a lot, the comparison of a character against NULL is weird (NULL is a pointer, not a character) and pointless since you've already checked with strlen():
void writeText(const unsigned char *string, int16_t x, int16_t y, uint16_t color,
uint16_t bgcolor, uint8_t size)
{
while(*string != '\0')
{
drawChar(x + (i * 6 * size), y, *string, color, bgcolor, size);
++string;
}
}
Also note the const; functions that take pointers to data that the functions only read should always be declared const.