-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path2310dealer.c
296 lines (247 loc) · 8.17 KB
/
2310dealer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#include "2310dealer.h"
/** Sets up pipes.
*
* @param sendPipe send fd's
* @param receivePipe receive fd's
* @param programName The child's programs' name
* @param numPlayers the number of players
* @param playerID the player's ID
*/
void child_setup(int sendPipe[2], int receivePipe[2], char *programName,
char *numPlayers, char *playerID) {
// write 2310A's stdout into pipe
dup2(sendPipe[WRITE_END], STDOUT_FILENO);
close(sendPipe[WRITE_END]);
close(sendPipe[READ_END]);
// 2310A reads from pipe2
dup2(receivePipe[READ_END], STDIN_FILENO);
close(receivePipe[READ_END]);
close(receivePipe[WRITE_END]);
// suppress stderr
int sink = fileno(fopen("/dev/null", "w"));
dup2(sink, STDERR_FILENO);
execl(programName, programName, numPlayers, playerID, NULL);
// if we get here, exec failed.
close(STDIN_FILENO);
close(STDOUT_FILENO);
// exit(0);
kill(getppid(), SIGABRT);
}
/** Closes pipes and creates reader/writers. **/
void create_reader_writers(Player *player) {
close(player->sendPipes[WRITE_END]);
close(player->receivePipes[READ_END]);
// write to this to talk to player
player->writer = fdopen(player->receivePipes[WRITE_END], "w");
// read from this to read from player
player->reader = fdopen(player->sendPipes[READ_END], "r");
}
/** Checks the stream for a carat. **/
int check_carat(Player *player, char *givenPath) {
char carat = fgetc(player->reader);
if (carat == '^') {
fprintf(player->writer, "%s\n", givenPath);
fflush(player->writer);
return 0;
}
dealer_exit(ERROR_STARTING_PLAYER);
return 1;
}
/** Checks if the deck file is valid. **/
DealerErrorCode check_deck_file(char *deckFileName) {
char *line = read_line(fopen(deckFileName, "r"));
// get number of cards
char *trash;
char buff[5];
snprintf(buff, 5, "%s", &line[0]);
int numberOfCards = (int) strtol(buff, &trash, 10);
if (numberOfCards < 4) {
dealer_exit(INVALID_DECK_FILE);
}
char *validChars = "ABCDE";
char str[2] = "\0";
for (int i = 1; i < strlen(line); ++i) {
str[0] = line[i];
if (strstr(validChars, str) == NULL) {
dealer_exit(INVALID_DECK_FILE);
}
if (i > numberOfCards) {
dealer_exit(INVALID_DECK_FILE);
}
}
return NO_ERROR;
}
/** Checks the dealer's args.
*
* @param argc
* @param argv
* @return NO_ERROR on success
*/
DealerErrorCode check_dealer_args(int argc, char **argv) {
char *deckFileName = argv[1];
char *mapFileName = argv[2];
// 9 players max + deck + path + program name
if (argc > 3 + 9) {
dealer_exit(INVALID_NUM_ARGS);
}
if (argc < 4) {
dealer_exit(INVALID_NUM_ARGS);
}
FILE *file = fopen(mapFileName, "r");
check_deck_file(deckFileName);
check_map_file(read_line(file), DEALER);
if (fgetc(file) == '\n') {
dealer_exit(INVALID_PATH_FILE);
}
return NO_ERROR;
}
/** Sets up all the forks.
*
* @param numPlayers Number of players in game
* @param path The game path
* @param argv argv
*/
void make_forks(int numPlayers, Path *path, char **argv) {
int cPID = fork();
char childArgsNumPlayers[2];
char childArgsID[2];
snprintf(childArgsNumPlayers, 2, "%d", numPlayers);
// if child
if (cPID == 0) {
for (int i = 1; i < numPlayers; ++i) {
// we are another clone
if (fork() == 0) {
snprintf(childArgsID, 2, "%d", i);
Player *player = path->players[i];
child_setup(player->sendPipes, player->receivePipes,
argv[i + 3], childArgsNumPlayers, childArgsID);
}
}
Player *player = path->players[0];
child_setup(player->sendPipes, player->receivePipes, argv[3],
childArgsNumPlayers, "0");
}
for (int i = 0; i < numPlayers; ++i) {
create_reader_writers(path->players[i]);
}
}
/** Initialises the dealer.
*
* @param givenPath The contents of the pathfile
* @param numPlayers The number of players
* @param deckFileName The deck file name
* @return an initialised Path
*/
Path *initialise_dealer(char *givenPath, int numPlayers, char *deckFileName) {
// do initialisation
Path *path = allocate_path(givenPath, numPlayers);
path->players = malloc(sizeof(Player *) * numPlayers);
path->deck = allocate_deck(fopen(deckFileName, "r"));
for (int i = 0; i < numPlayers; ++i) {
path->players[i] = init_player(path, i);
// make pipes
pipe(path->players[i]->sendPipes);
pipe(path->players[i]->receivePipes);
}
arrange_order_of_players(path);
return path;
}
/** Dealer program. **/
int main(int argc, char **argv) {
if ((intptr_t) signal(SIGABRT, sig_handler) == SIGABRT) {
dealer_exit(ERROR_STARTING_PLAYER);
}
if ((intptr_t) signal(SIGHUP, sig_handler) == SIGHUP) {
dealer_exit(COMMUNICATION_ERROR);
}
// arg checking
check_dealer_args(argc, argv);
char *deckFileName = argv[1];
char *mapFileName = argv[2];
// first three args are not players
int numPlayers = argc - 3;
char *givenPath = read_line(fopen(mapFileName, "r"));
Path *path = initialise_dealer(givenPath, numPlayers, deckFileName);
make_forks(numPlayers, path, argv);
start_dealer(path, givenPath);
return 0;
}
/** Sends HAPs to players and prints to dealer stdout. **/
void send_to_player(Path *path, Player *player, int oldPoints, int oldMoney,
int cardIndexHap) {
int playerId = player->id;
int newPoints = player->points - oldPoints;
int newMoney = player->money - oldMoney;
print_player_details(path, playerId, stdout);
fflush(stdout);
// send haps to players
for (int j = 0; j < path->playersInGame; ++j) {
fprintf(path->players[j]->writer, "HAP%d,%d,%d,%d,%d\n",
playerId, player->siteNumber, newPoints, newMoney,
cardIndexHap);
fflush(path->players[j]->writer);
}
}
/** Checks if all player have sent their carats. **/
void check_players_carats(Path *path, char *givenPath) {
for (int playerID = 0; playerID < path->playersInGame; ++playerID) {
if (check_carat(path->players[playerID], givenPath) == 1) {
exit(9);
}
}
}
/** Prints the endgame and sends endgame to players.
*
* @param path The game path
*/
void clean_up_game(Path *path) {
game_over(path, stdout);
// send DONE to players
for (int j = 0; j < path->playersInGame; ++j) {
fprintf(path->players[j]->writer, "DONE\n");
fflush(path->players[j]->writer);
}
}
/** Starts a dealer session.
*
* @param path The game path
* @param givenPath The contents of the path file
* @return 0 when game has ended
*/
int start_dealer(Path *path, char *givenPath) {
check_players_carats(path, givenPath);
print_path(path, stdout);
// main loop
while (check_game_over(path) != 0) {
int nextMovePlayerID = next_player_to_move(path);
fprintf(path->players[nextMovePlayerID]->writer, "YT\n");
fflush(path->players[nextMovePlayerID]->writer);
// wait for dealer input
char *input = read_line(path->players[nextMovePlayerID]->reader);
int playerID = next_player_to_move(path);
// recieved DO
char read[5];
char *ptr;
snprintf(read, 3, "%s", &input[0]);
// find sitenumber
if (strcmp("DO", read) == 0) {
snprintf(read, 5, "%s", &input[2]);
int siteNumber = (int) strtol(read, &ptr, 10);
Player *player = path->players[playerID];
// for HAP
int oldPoints = player->points;
int oldMoney = player->money;
move_player(path, playerID, siteNumber - player->siteNumber,
DEALER);
Site *site = &path->sites[player->siteNumber];
// do Ri type
int cardIndexHAP = 0;
if (strcmp(site->type, RI) == 0) {
cardIndexHAP = do_ri(path->deck, player);
}
send_to_player(path, player, oldPoints, oldMoney, cardIndexHAP);
}
}
clean_up_game(path);
return 0;
}