Slugger
Slugger

Reputation: 675

Keep executing loop while potentially taking input

I am trying to make a simple Tetris game in C. I have come to the input part and ran into trouble. I made a loop that moves the new piece down by every second. Now I want this to happen but I want the player to be able to push a button to move the piece, as is the object of the game of course. These two things should not interfere. The game loop is

  for (i=1;i<100;i++)
  {
    printBoard(&current);
    if (move(&current))
      spawn(&current);

    sleep(1);
  }

Here the function move, moves the falling block down one unit, and returns a 1 if the block hit the ground or another piece in which case a new block is spawned via the spawn function. printboard just prints the board to the terminal. What is the best way to do this?

Thanks in advance

For completeness, here is the full code so far

#include <stdio.h>
#include <math.h>

#define height 20
#define width 15

typedef struct board{
  int x[height][width];
  int score;
} board;

void printBoard(board * current){
  int i,j;
  for (i=0;i<40;i++)
    printf("\n");

  printf("Score : %d\n", current->score);

  for (i=0;i<height-1;i++)
  {
    printf("|");
    for (j=0;j<width;j++)
    { 
      if (current->x[i][j] == 0)
        printf(" ");
      else
        printf("x");
    }
    printf("|\n");
  }
  for (i=0;i<width+2;i++)
    printf("T");

  printf("\n");
}

void spawn(board * current){
  current->x[0][4] = 2;
  current->x[1][4] = 2;
  current->x[1][5] = 2;
  current->x[1][6] = 2;
  current->x[1][7] = 2;
}

int move(board * current){ // returns 1 if block hits ground
  int i,j;
  for (i=(height-1);i>0;i--)
    for (j=0;j<width;j++)
      if (current->x[i-1][j] == 2){
        if (i==(height-1) || current->x[i][j] == 1){
          goto DONE;
        }
        current->x[i][j] = 2;
        current->x[i-1][j] = 0;
      }


  return 0;

  DONE: 
  for (i=0;i<height;i++)
    for (j=0;j<width;j++)
      if (current->x[i][j] == 2)
        current->x[i][j] = 1;
      return 1;
}


int main(){
  board current;
  current.score = 0;
  int i,j;
  for (i=0;i<height;i++)
    for (j=0;j<width;j++)
      current.x[i][j] = 0;


  spawn(&current);
  for (i=1;i<100;i++)
  {
    printBoard(&current);
    if (move(&current))
      spawn(&current);

    sleep(1);
  }
  return 0;
}

Upvotes: 1

Views: 68

Answers (2)

Petr Skocik
Petr Skocik

Reputation: 60056

On a *nix, you can set your intput filedescriptor to a nonblocking mode. Then you can attempt a read at each refresh interval and it'll either succeed or fail with EAGAIN (assuming no error):

#include <unistd.h>        
#include <fcntl.h>
//...
fcntl(0 /*==stdin*/, F_SETFL, O_NONBLOCK); 

If your input is a terminal, you'll also need to disable the terminals line buffering (e.g., with cbreak() if you link ncurses).

select and family are useful if you've got nothing else to do other than wait on input. Looping in that case would waste CPU time that other processes could use.

Upvotes: 1

OznOg
OznOg

Reputation: 4722

the usual way to do that is to rely on a poll() or select() call.

the idea is to set a timeout on the select call (for you would be 1 second) and to perform the standard refresh when timeout occurs.

The user input is retrieved via a file descriptor (here I guess it would be stdin, thus 0) and each time the user pushes a button, the select exits but this time, the fd is marked as having inputs, thus you just have to read the input and treat what you've just read. and go again waiting in the select()

pseudo code would look like (take from the select man):

   int main(void)
   {
       fd_set rfds;
       struct timeval tv;
       int retval;

       /* look stdin (fd 0) for inputs */
       FD_ZERO(&rfds);
       FD_SET(0, &rfds);

       /* wait 1 second. */
       tv.tv_sec = 1;
       tv.tv_usec = 0;

       retval = select(1, &rfds, NULL, NULL, &tv);
       /* Carefull tv was updated (on linux) */

       if (retval == -1)
           perror(" an error occured");
       else if (retval == 1) {
           printf("Data is available on stdin \n");
           /* FD_ISSET(0, &rfds) should be true */
           handle_input();
       } else {
           printf("Timeout.\n");
          update_status();
       }
       exit(EXIT_SUCCESS);
   }

Upvotes: 1

Related Questions