| 270 static int sendmove(GameState *gamestate, GameInfo *gameinfo, |
270 static int sendmove(GameState *gamestate, GameInfo *gameinfo, |
| 271 int opponent, uint8_t mycolor) { |
271 int opponent, uint8_t mycolor) { |
| 272 |
272 |
| 273 size_t bufpos = 0; |
273 size_t bufpos = 0; |
| 274 char movestr[MOVESTR_BUFLEN]; |
274 char movestr[MOVESTR_BUFLEN]; |
| 275 bool remisrejected = false; |
275 bool remis_rejected = false; |
| |
276 bool remis_suggested = false; |
| |
277 bool use_premove = false; |
| 276 uint8_t code; |
278 uint8_t code; |
| |
279 |
| |
280 if (*gamestate->premove) { |
| |
281 use_premove = true; |
| |
282 const unsigned mlen = sizeof(gamestate->premove); |
| |
283 strncpy(movestr, gamestate->premove, mlen); |
| |
284 movestr[mlen] = '\0'; |
| |
285 memset(gamestate->premove, 0, mlen); |
| |
286 } |
| 277 |
287 |
| 278 flushinp(); |
288 flushinp(); |
| 279 while (1) { |
289 while (1) { |
| 280 if (timecontrol(gamestate, gameinfo)) { |
290 if (timecontrol(gamestate, gameinfo)) { |
| 281 net_send_code(opponent, NETCODE_TIMEOVER); |
291 net_send_code(opponent, NETCODE_TIMEOVER); |
| 282 return 1; |
292 return 1; |
| 283 } |
293 } |
| 284 |
294 |
| 285 move(inputy, 0); |
295 move(inputy, 0); |
| 286 if (remisrejected) { |
296 printw("Use chess notation to enter your move.\n"); |
| 287 printw( |
297 if (remis_suggested) { |
| 288 "Use chess notation to enter your move.\n" |
298 printw("The opponent offers remis. Also type remis to accept.\n\n"); |
| 289 "Remis offer rejected \n\n" |
299 } else if (remis_rejected) { |
| 290 "Type your move: "); |
300 printw("Remis offer rejected. \n\n"); |
| 291 } else { |
301 } else { |
| 292 printw( |
302 printw("Or use a command: remis, resign, savepgn \n\n"); |
| 293 "Use chess notation to enter your move.\n" |
303 } |
| 294 "Or use a command: remis, resign, savepgn\n\n" |
304 printw("Type your move: "); |
| 295 "Type your move: "); |
|
| 296 } |
|
| 297 clrtoeol(); |
305 clrtoeol(); |
| 298 |
306 |
| 299 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
307 /* check if the opponent sent us something */ |
| |
308 code = net_recieve_code_async(opponent); |
| |
309 switch (code) { |
| |
310 case NETCODE_REMIS: |
| |
311 remis_suggested = true; |
| |
312 break; |
| |
313 case NETCODE_RESIGN: |
| |
314 gamestate->resign = 1; |
| |
315 printw("\rYour opponent resigned!"); |
| |
316 clrtoeol(); |
| |
317 return 1; |
| |
318 case NETCODE_CONNLOST: |
| |
319 printw("\rYour opponent has left the game."); |
| |
320 clrtoeol(); |
| |
321 return 1; |
| |
322 case NETCODE_ERROR: |
| |
323 printw("\rCannot perform asynchronous network IO"); |
| |
324 cbreak(); getch(); |
| |
325 exit(EXIT_FAILURE); |
| |
326 case NETCODE_AGAIN: |
| |
327 /* try again */ |
| |
328 break; |
| |
329 default: |
| |
330 printw("\nThe opponent sent an invalid network pacakge."); |
| |
331 } |
| |
332 |
| |
333 /* read move */ |
| |
334 if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| |
335 use_premove = false; |
| 300 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
336 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
| 301 gamestate->resign = 1; |
337 gamestate->resign = 1; |
| 302 printw("You resigned!"); |
338 printw("You resigned!"); |
| 303 clrtoeol(); |
339 clrtoeol(); |
| 304 refresh(); |
340 refresh(); |
| 305 net_send_code(opponent, NETCODE_RESIGN); |
341 net_send_code(opponent, NETCODE_RESIGN); |
| 306 return 1; |
342 return 1; |
| 307 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
343 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
| 308 save_pgn(gamestate, gameinfo); |
344 save_pgn(gamestate, gameinfo); |
| 309 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
345 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
| 310 if (!remisrejected) { |
346 if (remis_suggested) { |
| |
347 net_send_code(opponent, NETCODE_REMIS); |
| |
348 gamestate->remis = 1; |
| |
349 printw("\rRemis accepted!"); |
| |
350 clrtoeol(); |
| |
351 refresh(); |
| |
352 return 1; |
| |
353 } if (!remis_rejected) { |
| 311 net_send_code(opponent, NETCODE_REMIS); |
354 net_send_code(opponent, NETCODE_REMIS); |
| 312 printw("Remis offer sent - waiting for acceptance..."); |
355 printw("Remis offer sent - waiting for acceptance..."); |
| 313 refresh(); |
356 refresh(); |
| 314 code = net_recieve_code(opponent); |
357 code = net_recieve_code(opponent); |
| 315 if (code == NETCODE_ACCEPT) { |
358 if (code == NETCODE_ACCEPT) { |
| 373 } |
416 } |
| 374 } |
417 } |
| 375 } |
418 } |
| 376 } |
419 } |
| 377 |
420 |
| 378 static int recvmove(GameState *gamestate, GameInfo *gameinfo, int opponent) { |
421 static int recvmove(GameState *gamestate, GameInfo *gameinfo, |
| 379 |
422 int opponent, uint8_t mycolor) { |
| 380 struct timeval timeout; |
423 memset(gamestate->premove, 0, sizeof(gamestate->premove)); |
| |
424 |
| |
425 size_t bufpos = 0; |
| |
426 char movestr[MOVESTR_BUFLEN]; |
| |
427 bool remis_suggested = false; |
| 381 while (1) { |
428 while (1) { |
| 382 timecontrol(gamestate, gameinfo); |
429 timecontrol(gamestate, gameinfo); |
| 383 |
430 |
| 384 move(inputy, 0); |
431 move(inputy, 0); |
| 385 printw("Awaiting opponent move..."); |
432 printw("Awaiting opponent move. Use chess notation to prepare a move.\n"); |
| |
433 if (*gamestate->premove) { |
| |
434 printw("Current pre-move: %s \n\n", |
| |
435 gamestate->premove); |
| |
436 } else if (remis_suggested) { |
| |
437 printw("Suggested remis. \n\n"); |
| |
438 } else { |
| |
439 printw("Or use a command: remis, resign, savepgn\n\n"); |
| |
440 } |
| |
441 printw("Prepare your next move: "); |
| 386 clrtoeol(); |
442 clrtoeol(); |
| 387 refresh(); |
443 refresh(); |
| 388 |
444 |
| 389 fd_set readfds; |
445 /* allow the player to prepare a move */ |
| 390 |
446 if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { |
| 391 FD_ZERO(&readfds); |
447 if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { |
| 392 FD_SET(opponent, &readfds); |
448 gamestate->resign = 1; |
| 393 timeout.tv_sec = 0; |
449 printw("You resigned!"); |
| 394 timeout.tv_usec = 100000; |
450 clrtoeol(); |
| 395 |
451 refresh(); |
| 396 // TODO: allow commands while waiting (e.g. resign, offer draw) |
452 net_send_code(opponent, NETCODE_RESIGN); |
| 397 |
|
| 398 int result = select(opponent+1, &readfds, NULL, NULL, &timeout); |
|
| 399 if (result == -1) { |
|
| 400 printw("\rCannot perform asynchronous network IO"); |
|
| 401 cbreak(); getch(); |
|
| 402 exit(EXIT_FAILURE); |
|
| 403 } |
|
| 404 if (result > 0) { |
|
| 405 uint8_t code = net_recieve_code(opponent); |
|
| 406 |
|
| 407 Move move; |
|
| 408 switch (code) { |
|
| 409 case NETCODE_TIMEOVER: |
|
| 410 /* redraw the time control */ |
|
| 411 timecontrol(gamestate, gameinfo); |
|
| 412 return 1; |
453 return 1; |
| 413 case NETCODE_RESIGN: |
454 } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { |
| 414 gamestate->resign = 1; |
455 remis_suggested = true; |
| 415 printw("\rYour opponent resigned!"); |
456 net_send_code(opponent, NETCODE_REMIS); |
| |
457 } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { |
| |
458 save_pgn(gamestate, gameinfo); |
| |
459 } else if (movestr[0] == 0) { |
| |
460 memset(gamestate->premove, 0, sizeof(gamestate->premove)); |
| |
461 } else { |
| |
462 Move move; |
| |
463 int res = eval_move(gamestate, movestr, &move, mycolor); |
| |
464 if (res == VALID_MOVE_SYNTAX) { |
| |
465 strncpy(gamestate->premove, movestr, 8); |
| |
466 memset(movestr, 0, MOVESTR_BUFLEN); |
| |
467 bufpos = 0; |
| |
468 clrtobot(); |
| |
469 } else { |
| |
470 eval_move_failed_msg(res); |
| |
471 } |
| |
472 } |
| |
473 } |
| |
474 |
| |
475 /* read opponent's move */ |
| |
476 uint8_t code = net_recieve_code_async(opponent); |
| |
477 switch (code) { |
| |
478 case NETCODE_TIMEOVER: |
| |
479 /* redraw the time control */ |
| |
480 timecontrol(gamestate, gameinfo); |
| |
481 return 1; |
| |
482 case NETCODE_RESIGN: |
| |
483 gamestate->resign = 1; |
| |
484 printw("\rYour opponent resigned!"); |
| |
485 clrtoeol(); |
| |
486 return 1; |
| |
487 case NETCODE_CONNLOST: |
| |
488 printw("\rYour opponent has left the game."); |
| |
489 clrtoeol(); |
| |
490 return 1; |
| |
491 case NETCODE_REMIS: |
| |
492 if (remis_suggested) { |
| |
493 gamestate->remis = 1; |
| |
494 printw("\rRemis accepted!"); |
| 416 clrtoeol(); |
495 clrtoeol(); |
| 417 return 1; |
496 return 1; |
| 418 case NETCODE_CONNLOST: |
497 } else { |
| 419 printw("\rYour opponent has left the game."); |
|
| 420 clrtoeol(); |
|
| 421 return 1; |
|
| 422 case NETCODE_REMIS: |
|
| 423 if (prompt_yesno( |
498 if (prompt_yesno( |
| 424 "\rYour opponent offers remis - do you accept")) { |
499 "\rYour opponent offers remis - do you accept")) { |
| 425 gamestate->remis = 1; |
500 gamestate->remis = 1; |
| 426 printw("\rRemis accepted!"); |
501 printw("\rRemis accepted!"); |
| 427 clrtoeol(); |
502 clrtoeol(); |
| 428 net_send_code(opponent, NETCODE_ACCEPT); |
503 net_send_code(opponent, NETCODE_ACCEPT); |
| 429 return 1; |
504 return 1; |
| 430 } else { |
505 } else { |
| 431 net_send_code(opponent, NETCODE_DECLINE); |
506 net_send_code(opponent, NETCODE_DECLINE); |
| 432 } |
507 } |
| 433 break; |
508 } |
| 434 case NETCODE_MOVE: |
509 break; |
| 435 net_recieve_data(opponent, &move, sizeof(Move)); |
510 case NETCODE_MOVE: { |
| 436 code = validate_move(gamestate, &move); |
511 Move move; |
| 437 if (code == VALID_MOVE_SEMANTICS) { |
512 net_recieve_data(opponent, &move, sizeof(Move)); |
| 438 apply_move(gamestate, &move); |
513 code = validate_move(gamestate, &move); |
| 439 if (gamestate->checkmate) { |
514 if (code == VALID_MOVE_SEMANTICS) { |
| 440 net_send_code(opponent, NETCODE_CHECKMATE); |
515 apply_move(gamestate, &move); |
| 441 printw("\rCheckmate!"); |
516 if (gamestate->checkmate) { |
| 442 clrtoeol(); |
517 net_send_code(opponent, NETCODE_CHECKMATE); |
| 443 return 1; |
518 printw("\rCheckmate!"); |
| 444 } else if (gamestate->stalemate) { |
519 clrtoeol(); |
| 445 net_send_code(opponent, NETCODE_STALEMATE); |
520 return 1; |
| 446 printw("\rStalemate!"); |
521 } else if (gamestate->stalemate) { |
| 447 clrtoeol(); |
522 net_send_code(opponent, NETCODE_STALEMATE); |
| 448 return 1; |
523 printw("\rStalemate!"); |
| 449 } else if (move.check) { |
524 clrtoeol(); |
| 450 net_send_code(opponent, NETCODE_CHECK); |
525 return 1; |
| 451 } else { |
526 } else if (move.check) { |
| 452 net_send_code(opponent, NETCODE_ACCEPT); |
527 net_send_code(opponent, NETCODE_CHECK); |
| 453 } |
|
| 454 return 0; |
|
| 455 } else { |
528 } else { |
| 456 uint32_t reason = htonl(code); |
529 net_send_code(opponent, NETCODE_ACCEPT); |
| 457 net_send_data(opponent, NETCODE_DECLINE, |
|
| 458 &reason, sizeof(uint32_t)); |
|
| 459 } |
530 } |
| 460 break; |
531 return 0; |
| 461 default: |
532 } else { |
| 462 printw("\nInvalid network request."); |
533 uint32_t reason = htonl(code); |
| 463 } |
534 net_send_data(opponent, NETCODE_DECLINE, |
| |
535 &reason, sizeof(uint32_t)); |
| |
536 } |
| |
537 break; |
| |
538 } |
| |
539 case NETCODE_ERROR: |
| |
540 printw("\rCannot perform asynchronous network IO"); |
| |
541 cbreak(); getch(); |
| |
542 exit(EXIT_FAILURE); |
| |
543 case NETCODE_AGAIN: |
| |
544 /* try again */ |
| |
545 break; |
| |
546 default: |
| |
547 printw("\nInvalid network request."); |
| 464 } |
548 } |
| 465 } |
549 } |
| 466 } |
550 } |
| 467 |
551 |
| 468 void game_review(Settings* settings, GameState *gamestate) { |
552 void game_review(Settings* settings, GameState *gamestate) { |