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.
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.
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 attempting to solve this problem but I'm not sure why my solution doesn't work. My attempts at debugging tell me that the solution is attempting to access indices outside of the bounds of some of the data structures, but this does not make sense to me as it seems like my for-loop test would would.
There are probably many other issues with this solution besides this.
I'm also 90% sure that there's a more efficient way to do this. Could you help me figure out what it is I've done wrong here?
If there is a more efficient solution, what would it be? I'm struggling to deal with keeping track of the same number of spaces in the same order in an efficient way.
If any more information is necessary, please let me know and I will update.
public static void printReversed(String line){
Scanner console = new Scanner(line);
ArrayList<String> list = new ArrayList<String>(); // keeps track of words in line
int spaceOccur = 0; // keeps track of the number of times there are spaces
while (console.hasNext()){
list.add(console.next());
spaceOccur++;
}
int[] spaces = new int[spaceOccur]; // keeps track of number of spaces for each occurrence of spaces
int count = 0; // for spaces[] traversal
// searches through original input to get number of spaces
for (int i = 0; i < line.length() - 1; i++){
if (line.charAt(i) == ' '){
int j = i;
int num = 0;
// traversal through spaces to count how many
while (line.charAt(j) == (' ')){ // first error here
num++;
j++;
}
i = j; // updates for loop counter to point past spaces
spaces[count] = num; // saves number of spaces
count++;
}
}
// printing reversed input
for (int k = 0; k < list.size(); k++){
// prints reversed chars
for (int m = list.get(k).length(); m > 0; m++){
System.out.print(list.get(k).charAt(m));
}
// prints spaces
for (int n = 0; n < spaces[k]; n++){
System.out.print(" ");
}
}
}
I'd say that you're on right tracks, but some places need some closer inspection. The first loop seems to have some problems: The j++ is probably the one that goes beyond boundaries of the array - at least if you have spaces at the end of your string. And the whole loop itself seems to ignore the last character of the line.
Are you sure you even need this first loop? If I have understood correctly, the ScannerĀ“s next() will give you strings between the spaces; in the case of two consecutive spaces I think it should return you an empty string. In this case you could just loop the list the way you do in the end of your function, and print a space character when you encounter an empty string in your list. Otherwise just print the word backwards, just like you already do (except that it should be m-- instead of m++ in the last for loop).
But if the Scanner won't give you the empty strings when there are two or more consecutive space characters, I bet the string's split() method should work.
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.
I'm learning C, so decided to try and make a Tic Tac Toe game, using ASCII art as the table.
I don't have much yet...
#include <stdio.h>
#define WIDTH 2;
#define HEIGHT 2;
int main (int argc, char *argv[]) {
printf("Welcome to Tic Tac Toe!\n\n");
int width = WIDTH;
int height = HEIGHT;
// Make grid
for (int y = 0; y <= height; y++) {
for (int x = 0; x <= width; x++) {
printf("%d%d", x, y);
if (x != width) {
printf("||");
}
}
if (y != height) {
printf("\n");
for (int i = 0; i < (width + (width * 4)); i++) {
printf("=");
}
printf("\n");
} else {
printf("\n");
}
}
// Ask for user input
printf("Please enter the cell where you would like to place an X, e.g. for example the first top left cell is '00'\n");
}
When ran on the command line, I get this output
Welcome to Tic Tac Toe!
00||10||20
==========
01||11||21
==========
02||12||22
Please enter the cell where you would like to place an X, e.g. for example the first top left cell is '00'
Now, when I figure out how to get input of more than one char (I only know how to use getchar() for getting individual chars so far, though that might work OK for this example), I'd like to loop through again and place an X for the corresponding cell.
Should I write a function for printing the table, which takes arguments such as 'int markerX, int markerY` to place the X?
How would I then store the position of the marker so I could check if the game is won or not?
Is my Choose which cell to place marker the best way to ask for user input for a game on the command line?
Thanks!
Right now you've done a pretty good job of figuring out how to output a tic-tac toe board to the screen, but what you are currently lacking is a model of your tic-tac board in memory that your program can modify and use to reprint the current status of the board.
Typically, whenever you have a 2 dimensional grid of elements to model, the simplest model to represent these is a 2-dimensional array. You can define one like this:
int board[WIDTH][HEIGHT]
Then you can access the current state of any element on the board by referencing it by it's index (which always goes from 0-1 less than the number of elements in the array, like so:
int element = board[0][1]
To re-output the board to the user, you may want to implement a function that prints out the board, including its current state. Once that function exists, you can always call that in your code without needing to repeat yourself.
Finally, you'll need to implement a loop. while is the structure most suited to this - it loops until a condition is true.
int shouldExit = 0;
while (shouldExit == 0) {
printBoard();
getPlayerInput();
updateBoard();
// you can set shouldExit to 1 if the game is over
// (someone won, or the player inputted something that indicates they want to quit.
}
I would create a function whose only job was to print the board, like this (untested):
void board_print(const char b[3][3]) }
int i = 1;
printf( " A B C\n"); // NOTE: Letters on top, like a spreadsheet
while (1) {
printf( "%i %c | %c | %c\n", i, b[i][0], b[i][1], b[i][2]);
if (++i <=3) {
printf( " ---+---+---\n");
} else {
break;
}
}
}
And then create a board like this:
int main(void) {
char board[3][3] =
{{ ' ', ' ', ' ' },
{ ' ', ' ', ' ' },
{ ' ', ' ', ' ' }};
while (1) {
board_print(board);
printf("Place X at : ");
Then I would create another function whose entire job is to attempt to place an X or an O in a position:
int board_place(char board[3][3], char marker, char row, char column);
marker here would either be an 'x' or an 'o'.
board_place could return a value which you can use to determine the general state of play. For instance if board_place could not make the move because there was already a marker in that position then it should return a distinct error code from if the row was out of range. This could also be where you determine if there is a winner (because the current move would be the winning move {easy since you only have to test for lines made including the current square}) or if the board is full with no winner and return distinct things for each of these.
Then your main code just decides what to do based on the return value of board_place (issue an error message to the user and prompt for another position, try to make a move for O, congratulate the user on victory, tell the user that the cat won that one, ...).
As far as reading in the position, that's where user interface meets text processing. A simple scanf call could be enough, or maybe just a few getchar calls. I do suggest that you index the rows and columns differently (I used letters and numbers) because that would probably be easier for most people. Then you can either adjust before board_place or just handle adjusting the indexes within board_place.
It's probably a good idea to store your game status in some data structure--I'd recommend a two-dimensional array of chars that store whether player 1 or player 2 has placed a marker there.
char board[WIDTH][HEIGHT];
Then after you initialize each element in the array, you can access whether or not there is a marker there with
board[1][2]
for example if you wanted to get the marker at 1, 2.
Now each time the board changes, you can write a function that prints out the board again using a nested for-loop like you did before, except now you'll be accessing the 2d array instead.
You can't currently reference the cells because you don't store the values anywhere. You need to create a variable to store the value in a cell. To modify the value of a cell all you would need to do is change the value of the associated variable and re-draw the table.
To make things easier, you might write a draw_table function that draws the tic-tac-toe board using the current value of your variables.
If you are only using getch for input, then you might refer to your cells as 1-9 instead of using a two-digit number. This might also make it easier to keep track of your variables, you can call them cell1, cell2, etc. You can also use an array, but that might be too complicated if you are just starting out.