#include #include #include #include #include #include #include #include #include "kalaha.h" #define PORT 4711 int Write (int fd, char * buf, int len) { int ret; while (len) { ret = write (fd, buf, len); if (ret >= 0) { buf += ret; len -= ret; continue; } /* Error on write */ if ((errno == EAGAIN) || (errno == EINTR)) { continue; } perror ("write"); return 0; } return 1; } void Abort (int * playerfds) { int i; for (i=0; i<2; ++i) { Write (playerfds[i], "ABORT\r\n", 7); close (playerfds[i]); } exit (1); } void do_state (int * pfds, kalaha_state * state) { char buf[1000]; char * p = buf; char me[3] = "A\r\n"; int i; sprintf (p, "Machine:"); p += 8; for (i=0; i<14; ++i) { assert (state->holes[i] < 1000); sprintf (p, "%4d", state->holes[i]); p += 4; } sprintf (p, "\r\nHuman:\r\n"); p += 10; sprintf (p, " B6 B5 B4 B3 B2 B1\r\n"); p += 30; sprintf (p, " B7"); p += 4; for (i=B6; i >= B1; --i) { sprintf (p, "%4d", state->holes[i]); p += 4; } sprintf (p, "\r\n%4d %4d\r\n", state->holes[B7], state->holes[A7]); p += 36; sprintf (p, " "); p += 4; for (i=A1; i <= A6; ++i) { sprintf (p, "%4d", state->holes[i]); p += 4; } sprintf (p, " A7\r\n A1 A2 A3 A4 A5 A6\r\n"); p += 36; sprintf (p, ".\r\n"); p += 3; for (i=0; i<2; i++,me[0]++) { if (!Write (pfds[i], "STATE\r\nPlayer: ", 15)) Abort (pfds); if (!Write (pfds[i], me, 3)) Abort (pfds); if (!Write (pfds[i], "Move: ", 6)) Abort (pfds); if (i == state->player) { if (!Write (pfds[i], "yes\r\n", 5)) Abort (pfds); } else { if (!Write (pfds[i], "no\r\n", 4)) Abort (pfds); } if (!Write (pfds[i], buf, p-buf)) Abort (pfds); } } int main () { int sockfd, playerfds[2], port, player, i, move, failed, resigned = 0; FILE * playerfiles[2]; kalaha_state * state; struct sockaddr_in addr; char buf[100]; sockfd = socket (PF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror ("socket"); exit (1); } memset (&addr, 0, sizeof (struct sockaddr_in)); port = PORT; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl (INADDR_ANY); while (1) { addr.sin_port = htons(port); if (bind (sockfd, (struct sockaddr*)&addr, sizeof (struct sockaddr_in)) >= 0) break; /* Error binding to local address ...*/ if (errno == EADDRINUSE) { port++; continue; } /* ... because of something else than address in use. */ perror ("bind"); exit (1); } if (listen (sockfd, 5) < 0) { perror ("listen"); exit (1); } printf ("Listening on Port %d\n", addr.sin_port); /* Wait for two connection from the players. */ for (i=0; i<2; ++i) { while (1) { playerfds[i] = accept(sockfd, NULL, NULL); if (playerfds[i] >= 0) break; /* Error in accept */ if ((errno == EINTR) || (errno == EAGAIN)) continue; perror ("accept"); if (i) { write (playerfds[0], "ABORT\r\n", 7); close (playerfds[0]); } exit (1); } } /* Close the listening socket. We got all of our connections. */ close (sockfd); /* Tell the clients that we're set. */ if ((Write (playerfds[0], "OK\r\n", 4) == 0) || (Write (playerfds[1], "OK\r\n", 4) == 0)) { Abort (playerfds); } state = kalaha_create (PLAYERA); /* Open only for reading. This allows us to use unbuffered * writes with the filedescriptors. Note that we close the * filedescriptors and not the files. */ playerfiles[0] = fdopen (playerfds[0], "r"); playerfiles[1] = fdopen (playerfds[1], "r"); while (1) { /* Save the player we're currently dealing with because * state->player may change during this loop. */ player = state->player; /* Tell both players the current state */ do_state (playerfds, state); if (resigned || kalaha_end (state)) { int res[2], winner; kalaha_collect (state); res[0] = state->holes[A7]; res[1] = state->holes[B7]; winner = (res[0]>res[1])?'A':'B'; for (i=0; i<2; ++i) { if (res[0] == res[1]) { sprintf (buf, "END DRAW %d:%d\r\n", res[0], res[1]); } else { sprintf (buf, "END WINNER %c %d:%d\r\n", winner, res[i], res[1-i]); } if (!Write (playerfds[i], buf, strlen(buf))) Abort (playerfds); } state->player = PLAYERNONE; do_state (playerfds, state); break; } /* Tell the player who's turn it is that he should proceed. */ if (!Write (playerfds[player], "TURN\r\n", 6)) Abort (playerfds); /* Get a move from the player and try to execute it. */ while (1) { /* Try to read one line from the client */ fgets (buf, 100, playerfiles[player]); if (feof (playerfiles[player])) Abort (playerfds); failed = 0; /* Did we get a complete line that fits into the * buffer ? */ if (strchr (buf, '\n') == NULL) failed = 1; /* Skip rest of line in case we didn't get a * complete line. */ if (!failed && (strncasecmp (buf, "quit", 4) == 0)) { kalaha_collect (state); switch (player) { case PLAYERA: state->holes[B7] += state->holes[A7] ; state->holes[A7] = 0; break; case PLAYERB: state->holes[A7] += state->holes[B7] ; state->holes[B7] = 0; break; default: assert (0); } resigned = 1; break; } while (strchr (buf, '\n') == NULL) { fgets (buf, 100, playerfiles[player]); if (feof (playerfiles[player])) Abort (playerfds); } /* Try to read the move from the buffer. */ if (!failed && (sscanf (buf, "%d", &move) != 1)) failed = 1; /* Try to execute the move now */ if (!failed && kalaha_move (state, move)) break; /* We were unable to execute the move. */ if (!Write (playerfds[player], "FAILED\r\n", 8)) Abort (playerfds); /* Try again */ } /* while */ /* We got a move and executed it successfully. Tell the * player that everything was ok. Note that a resignation * counts as a successful move. */ if (!Write (playerfds[player], "OK\r\n", 4)) Abort (playerfds); if (resigned) { sprintf (buf, "MOVE %c QUITS\r\n", 'A'+player); } else { sprintf (buf, "MOVE %c%d\r\n", 'A'+player, move); } for (i=0; i<2; ++i) { if (!Write (playerfds[i], buf, strlen (buf))) Abort (playerfds); } /* Done, go get the next move. */ } /* while */ close (playerfds[0]); close (playerfds[1]); return 0; }