/* server.c * * Main game engine for a simple 2D grid game by Robert Marmorstein. * * Copyright (c) 2012 Robert Marmorstein (marmorsteinrm@longwood.edu) * Released under the terms of the GPL, version 2. See LICENSE file for details. * * For questions about the licensing of this software, contact me by mail: * Ruffner 329, Longwood University, 201 High Street, Farmville, VA 23909 */ #include #include #include #include #include #include #include #include "config.h" #include "shmlib.h" #include "commands.h" //Pipe for communicating with the "ghosts" int fd[NUM_GHOSTS][2]; int pids[NUM_GHOSTS]; //Positions of the ghosts int ghost_x[NUM_GHOSTS]; int ghost_y[NUM_GHOSTS]; //Position and score of the player int player_x; int player_y; int score; //Clear map and give it a border void init_map(char* grid) { int i; for (i=0;i " sprintf(arg1, "%d", fd[ghost][1]); sprintf(arg2, "%d", ghost_x[ghost]); sprintf(arg3, "%d", ghost_y[ghost]); close(fd[ghost][0]); execl("./client", "./client", arg1, arg2, arg3, NULL); } close(fd[ghost][1]); } } //Respond to a movement command from the "ghost" process. int do_cmd(char* grid, int ghost, int cmd) { int new_x = ghost_x[ghost]; int new_y = ghost_y[ghost]; //Erase ghost at the old position grid[new_x + WIDTH*new_y] = '.'; //Adjust x and y coordinates if (cmd == NORTH) { --new_y; } else if (cmd == EAST) { ++new_x; } else if (cmd == SOUTH) { ++new_y; } else if (cmd == WEST) { --new_x; } //As long as ghost is still on the map, move it if (new_x >= 1 && new_x < WIDTH-1 && new_y >=1 && new_y < HEIGHT-1) { ghost_x[ghost] = new_x; ghost_y[ghost] = new_y; //If the ghost has moved over the treasure, reposition the treasure if (grid[new_x + WIDTH*new_y] == '$') { place_treasure(grid); } else if (grid[new_x + WIDTH*new_y] == '+') { //If the ghost has moved over the player, return 1 to indicate that the player has died. return 1; } //Draw the ghost at the new position. grid[new_x + WIDTH*new_y] ='?'; } return 0; } //Respond to a key press int do_player_cmd(char* grid, char key) { int new_x = player_x; int new_y = player_y; //Erase the player at the old position grid[new_x + WIDTH*new_y] = '.'; //Update the player's coordinates if (key == 'i') { --new_y; } else if (key == 'l') { ++new_x; } else if (key == 'm') { ++new_y; } else if (key == 'j') { --new_x; } //If the player hasn't moved off the map if (new_x >= 1 && new_x < WIDTH-1 && new_y >=1 && new_y < HEIGHT-1) { player_x = new_x; player_y = new_y; if (grid[new_x + WIDTH*new_y] == '$') { //If the player got the treasure, increase their score place_treasure(grid); ++score; } else if (grid[new_x + WIDTH*new_y] == '?') { //If the player moved on top of a ghost, game over. return 1; } //Draw the player at the new position grid[new_x + WIDTH*new_y] ='+'; } return 0; } //The main loop of the program, which uses "select" to do I/O multiplexing. void main_loop(char* grid) { fd_set orig_set; fd_set read_set; fd_set except_set; int game_over; int max_fd; int nfds; int ghost; int count = 0; FD_ZERO(&orig_set); FD_SET(0,&orig_set); max_fd = 0; for (ghost=0; ghost < NUM_GHOSTS; ++ghost) { FD_SET(fd[ghost][0], &orig_set); if (fd[ghost][0] > max_fd) max_fd = fd[ghost][0]; } game_over = 0; //Main game loop do { print_map(grid); read_set = orig_set; except_set = orig_set; nfds = select(max_fd + 1, &read_set, NULL, &except_set, NULL); if (nfds < 0) { perror("select"); exit(-1); } if (FD_ISSET(0, &read_set)) { char key; //Read a keypress from the keyboard read(0, &key, sizeof(char)); game_over += do_player_cmd(grid, key); } //Loop through the ghosts, read a command from each one, and "do" the command. for (ghost = 0; ghost < NUM_GHOSTS; ++ghost) { if (FD_ISSET(fd[ghost][0], &read_set)) { int cmd; read(fd[ghost][0], &cmd, sizeof(int)); if (cmd == CMD_QUIT) { ++count; FD_CLR(fd[ghost][0], &orig_set); } else { game_over += do_cmd(grid, ghost, cmd); } } else if (FD_ISSET(fd[ghost][0], &except_set)) { printf("Error on ghost %d's fd.\n", ghost); FD_CLR(fd[ghost][0], &orig_set); } } } while (game_over == 0); } int main(int argc, char* argv[]) { char* grid; int status; int ghost; struct termios term_settings; struct termios old_settings; srandom(time(NULL)); //Remember the current terminal settings, so we can restore them. tcgetattr(0, &term_settings); old_settings = term_settings; //Then set up the terminal so that we don't need to press enter after every command: //1. Turn off buffering setbuf(stdin, NULL); //2. Turn off Echo, EchoNewLine, and Canonical Mode (see manpage for tcsetattr) term_settings.c_lflag &= ~(ECHO | ECHONL | ICANON ); //3. Engage new settings tcsetattr(0, TCSANOW, &term_settings); //Share the grid with the client grid = attach_map(1, WIDTH,HEIGHT); //Initialize the grid. init_map(grid); //Place an initial treasure place_treasure(grid); //Place the ghosts for (ghost = 0; ghost < NUM_GHOSTS; ++ghost) place_ghost(grid, ghost); //Place the player place_player(grid); //Create the pipes launch_clients(); //Let 'er rip! main_loop(grid); //Player has been munched, print "end of game" message. printf("Game Over! Score: %d\n", score); for (ghost = 0; ghost < NUM_GHOSTS; ++ghost) kill(pids[ghost], SIGHUP); //Restore old terminal settings tcsetattr(0, TCSANOW, &old_settings); }