Thu, 28 May 2026 12:15:26 +0200
simplify code structure
| 0 | 1 | /* |
| 2 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. | |
| 3 | * | |
|
55
54ea19938d57
updated copyright and version info
Mike Becker <universe@uap-core.de>
parents:
52
diff
changeset
|
4 | * Copyright 2016 Mike Becker. All rights reserved. |
| 0 | 5 | * |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions are met: | |
| 8 | * | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * | |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 13 | * notice, this list of conditions and the following disclaimer in the | |
| 14 | * documentation and/or other materials provided with the distribution. | |
| 15 | * | |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| 26 | * POSSIBILITY OF SUCH DAMAGE. | |
| 27 | * | |
| 28 | */ | |
| 29 | ||
|
92
84e0dec5db16
use port number instead of port string in network code
Mike Becker <universe@uap-core.de>
parents:
90
diff
changeset
|
30 | #define PROGRAM_VERSION "1.0 alpha" |
| 78 | 31 | |
| 129 | 32 | #include "chess/rules.h" |
| 33 | #include "chess/pgn.h" | |
|
7
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
34 | #include "input.h" |
| 78 | 35 | #include "network.h" |
| 35 | 36 | #include "colors.h" |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
37 | #include <string.h> |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
38 | #include <time.h> |
|
7
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
39 | #include <getopt.h> |
| 69 | 40 | #include <locale.h> |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
41 | #include <sys/stat.h> |
| 129 | 42 | #include <signal.h> |
| 43 | #include <errno.h> | |
| 44 | #include <unistd.h> | |
| 45 | ||
| 46 | typedef struct { | |
| 47 | GameInfo gameinfo; | |
| 48 | /** | |
| 49 | * Server host address. | |
| 50 | * TCP: server address or \c NULL when we are the server | |
| 51 | * Domain Socket: the path to the domain socket | |
| 52 | */ | |
| 53 | char* serverhost; | |
| 54 | char* continuepgn; | |
| 55 | short port; | |
| 56 | bool ishost; | |
| 57 | bool usedomainsocket; | |
| 58 | bool singlemachine; | |
| 59 | bool disableflip; | |
| 60 | bool unicode; | |
| 61 | } Settings; | |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
62 | |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
63 | int get_settings(int argc, char **argv, Settings *settings) { |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
64 | char *valid; |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
65 | unsigned long int time, port; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
66 | uint8_t timeunit = 60; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
67 | size_t len; |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
68 | bool port_set = false; |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
69 | |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
70 | for (int opt ; (opt = getopt(argc, argv, "a:bc:Fhp:rsS:t:uUv")) != -1 ;) { |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
71 | switch (opt) { |
|
47
d726e4b46c33
refactoring of getlocation mechanism for better short algebraic notation support (does now respect pinned pieces) + fixed a bug where a pawn could advance through a piece (e.g. e2e4 could jump over a piece on e3)
Mike Becker <universe@uap-core.de>
parents:
46
diff
changeset
|
72 | case 'c': |
|
d726e4b46c33
refactoring of getlocation mechanism for better short algebraic notation support (does now respect pinned pieces) + fixed a bug where a pawn could advance through a piece (e.g. e2e4 could jump over a piece on e3)
Mike Becker <universe@uap-core.de>
parents:
46
diff
changeset
|
73 | settings->continuepgn = optarg; |
|
d726e4b46c33
refactoring of getlocation mechanism for better short algebraic notation support (does now respect pinned pieces) + fixed a bug where a pawn could advance through a piece (e.g. e2e4 could jump over a piece on e3)
Mike Becker <universe@uap-core.de>
parents:
46
diff
changeset
|
74 | break; |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
75 | case 'b': |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
76 | settings->gameinfo.servercolor = BLACK; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
77 | break; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
78 | case 'r': |
|
7
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
79 | settings->gameinfo.servercolor = rand() & 1 ? WHITE : BLACK; |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
80 | break; |
|
26
e0a76ee1bb2b
introduced single machine mode
Mike Becker <universe@uap-core.de>
parents:
23
diff
changeset
|
81 | case 's': |
|
90
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
82 | settings->singlemachine = true; |
|
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
83 | break; |
|
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
84 | case 'F': |
|
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
85 | settings->disableflip = true; |
|
26
e0a76ee1bb2b
introduced single machine mode
Mike Becker <universe@uap-core.de>
parents:
23
diff
changeset
|
86 | break; |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
87 | case 'u': |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
88 | if (port_set) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
89 | fprintf(stderr, "Cannot use Unix domain sockets " |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
90 | "when a TCP port was specified.\n"); |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
91 | return 1; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
92 | } |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
93 | settings->usedomainsocket = true; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
94 | break; |
| 69 | 95 | case 'U': |
|
90
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
96 | settings->unicode = false; |
| 69 | 97 | break; |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
98 | case 't': |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
99 | case 'a': |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
100 | len = strlen(optarg); |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
101 | if (optarg[len-1] == 's') { |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
102 | optarg[len-1] = '\0'; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
103 | timeunit = 1; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
104 | } |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
105 | |
|
30
a285ee393860
experimental async input for single machine mode
Mike Becker <universe@uap-core.de>
parents:
29
diff
changeset
|
106 | if ((time = strtoul(optarg, &valid, 10))*timeunit > UINT16_MAX |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
107 | || *valid != '\0') { |
|
30
a285ee393860
experimental async input for single machine mode
Mike Becker <universe@uap-core.de>
parents:
29
diff
changeset
|
108 | fprintf(stderr, "Specified time is invalid (%s)" |
|
a285ee393860
experimental async input for single machine mode
Mike Becker <universe@uap-core.de>
parents:
29
diff
changeset
|
109 | "- Maximum: 65535 seconds (1092 minutes)\n", optarg); |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
110 | return 1; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
111 | } else { |
|
30
a285ee393860
experimental async input for single machine mode
Mike Becker <universe@uap-core.de>
parents:
29
diff
changeset
|
112 | settings->gameinfo.timecontrol = 1; |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
113 | if (opt=='t') { |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
114 | settings->gameinfo.time = timeunit * time; |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
115 | } else { |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
116 | settings->gameinfo.addtime = time; |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
117 | } |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
118 | } |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
119 | break; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
120 | case 'p': |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
121 | if (port_set) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
122 | fprintf(stderr, "Cannot use -p twice.\n"); |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
123 | return 1; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
124 | } |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
125 | if (settings->usedomainsocket) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
126 | fprintf(stderr, "Cannot specify TCP port " |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
127 | "when using Unix domain sockets.\n"); |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
128 | return 1; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
129 | } |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
130 | port = strtol(optarg, &valid, 10); |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
131 | if (port < 1025 || port > 65535 || *valid != '\0') { |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
132 | fprintf(stderr, |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
133 | "Invalid port number (%s) - choose a number between " |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
134 | "1025 and 65535\n", |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
135 | optarg); |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
136 | return 1; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
137 | } else { |
|
92
84e0dec5db16
use port number instead of port string in network code
Mike Becker <universe@uap-core.de>
parents:
90
diff
changeset
|
138 | settings->port = (short) port; |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
139 | port_set = true; |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
140 | } |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
141 | break; |
| 52 | 142 | case 'v': |
| 143 | printf("terminal-chess : Version %s (Netcode Version %d)\n", | |
| 144 | PROGRAM_VERSION, NETCODE_VERSION); | |
| 145 | exit(0); | |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
146 | case 'h': |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
147 | case '?': |
| 52 | 148 | printf( |
| 149 | "Usage: terminal-chess [OPTION]... [HOST]\n" | |
| 150 | "Starts/joins a network chess game\n" | |
| 151 | "\nGeneral options\n" | |
| 152 | " -c <PGN file> Continue the specified game\n" | |
| 153 | " -h This help page\n" | |
| 154 | " -p TCP port to use (default: 27015)\n" | |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
155 | " -u Use Unix domain socket instead of TCP\n" |
|
84
df47b7efb97d
disabling Unicode pieces is a general option
Mike Becker <universe@uap-core.de>
parents:
83
diff
changeset
|
156 | " -U Disables unicode pieces\n" |
| 75 | 157 | " -v Print version information and exits\n" |
| 52 | 158 | "\nServer options\n" |
| 159 | " -a <time> Specifies the time to add after each move\n" | |
| 160 | " -b Server plays black pieces (default: white)\n" | |
| 161 | " -r Distribute color randomly\n" | |
| 162 | " -t <time> Specifies time limit (default: no limit)\n" | |
|
90
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
163 | "\nHot seat\n" |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
164 | " -s Play a hot seat game (network options are ignored)\n" |
|
90
395542d915d7
add option that disables board flip in solo games
Mike Becker <universe@uap-core.de>
parents:
89
diff
changeset
|
165 | " -F Do not automatically flip the board in hot seat games\n" |
| 52 | 166 | "\nNotes\n" |
| 167 | "The time unit for -a is seconds and for -t minutes by default. To " | |
| 168 | "specify\nseconds for the -t option, use the s suffix.\n" | |
| 169 | "Example: -t 150s\n\n" | |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
170 | "Use '-' for PGN files to read PGN data from standard input\n\n" |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
171 | "When playing over Unix domain socket, the HOST denotes the socket path.\n" |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
172 | "When the path doest not exist, a game is created. Otherwise, the program\n" |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
173 | "joins the existing game. When HOST is omitted, /tmp/chess.sock is used.\n" |
| 52 | 174 | ); |
| 175 | exit(0); | |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
176 | } |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
177 | } |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
178 | |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
179 | if (optind == argc - 1) { |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
180 | settings->serverhost = argv[optind]; |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
181 | } else if (optind < argc - 1) { |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
182 | fprintf(stderr, "Too many arguments\n"); |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
183 | return 1; |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
184 | } |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
185 | |
|
50
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
186 | |
|
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
187 | if (settings->continuepgn) { |
|
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
188 | if (settings->serverhost) { |
|
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
189 | fprintf(stderr, "Can't continue a game when joining a server.\n"); |
|
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
190 | return 1; |
|
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
191 | } |
|
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
192 | } |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
193 | |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
194 | if (settings->usedomainsocket) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
195 | if (!settings->serverhost) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
196 | settings->serverhost = "/tmp/chess.sock"; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
197 | } |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
198 | struct stat st; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
199 | if (stat(settings->serverhost, &st) == 0) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
200 | if (S_ISSOCK(st.st_mode)) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
201 | settings->ishost = false; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
202 | } else { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
203 | fprintf(stderr, "%s is not a Unix domain socket.\n", |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
204 | settings->serverhost); |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
205 | return 1; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
206 | } |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
207 | } else { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
208 | settings->ishost = true; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
209 | } |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
210 | } else { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
211 | settings->ishost = !settings->serverhost; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
212 | } |
|
50
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
213 | |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
214 | return 0; |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
215 | } |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
216 | |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
217 | static Settings settings; |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
218 | |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
219 | static void init_settings(void) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
220 | memset(&settings, 0, sizeof(settings)); |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
221 | settings.gameinfo.servercolor = WHITE; |
|
92
84e0dec5db16
use port number instead of port string in network code
Mike Becker <universe@uap-core.de>
parents:
90
diff
changeset
|
222 | settings.port = 27015; |
| 69 | 223 | settings.unicode = !!setlocale(LC_CTYPE, "C.UTF-8"); |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
224 | } |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
225 | |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
226 | static void cleanup() { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
227 | endwin(); |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
228 | if (settings.usedomainsocket && settings.ishost) { |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
229 | remove(settings.serverhost); |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
230 | } |
|
1
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
231 | } |
|
e5fbb8f9edbe
added (single) client / server architecture
Mike Becker <universe@uap-core.de>
parents:
0
diff
changeset
|
232 | |
| 129 | 233 | static const uint8_t boardx = 4, boardy = 10; |
| 234 | static int inputy = 21; /* should be overridden on game startup */ | |
| 235 | ||
| 236 | static int timecontrol(GameState *gamestate, GameInfo *gameinfo) { | |
| 237 | if (gameinfo->timecontrol) { | |
| 238 | uint16_t white = remaining_movetime(gameinfo, gamestate, WHITE); | |
| 239 | uint16_t black = remaining_movetime(gameinfo, gamestate, BLACK); | |
| 240 | char clkstr[16]; | |
| 241 | bool always_hours = gameinfo->time >= 3600; | |
| 242 | print_clk(white, clkstr, always_hours); | |
| 243 | mvprintw(boardy+4, boardx-1, "White time: %s", clkstr); | |
| 244 | print_clk(black, clkstr, always_hours); | |
| 245 | mvprintw(boardy+5, boardx-1, "Black time: %s", clkstr); | |
| 246 | ||
| 247 | if (white == 0) { | |
| 248 | move(inputy, 0); | |
| 249 | printw("Time is over - Black wins!"); | |
| 250 | clrtobot(); | |
| 251 | refresh(); | |
| 252 | return 1; | |
| 253 | } | |
| 254 | if (black == 0) { | |
| 255 | move(inputy, 0); | |
| 256 | printw("Time is over - White wins!"); | |
| 257 | clrtobot(); | |
| 258 | refresh(); | |
| 259 | return 1; | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | return 0; | |
| 264 | } | |
| 265 | ||
| 266 | static void draw_board(GameState *gamestate, | |
| 267 | uint8_t perspective, | |
| 268 | bool unicode) { | |
| 269 | char fen[90]; | |
| 270 | compute_fen(fen, gamestate); | |
| 271 | mvaddstr(0, 0, fen); | |
| 272 | ||
| 273 | for (uint8_t y = 0 ; y < 8 ; y++) { | |
| 274 | for (uint8_t x = 0 ; x < 8 ; x++) { | |
| 275 | uint8_t col = gamestate->board[y][x] & COLOR_MASK; | |
| 276 | uint8_t piece = gamestate->board[y][x]; | |
| 277 | char piecestr[5]; | |
| 278 | if (piece) { | |
| 279 | if (unicode) { | |
| 280 | char* uc = getpieceunicode(piece); | |
| 281 | strncpy(piecestr, uc, 5); | |
| 282 | } else { | |
| 283 | piecestr[0] = (piece & PIECE_MASK) == PAWN | |
| 284 | ? 'P' : getpiecechr(piece); | |
| 285 | piecestr[1] = '\0'; | |
| 286 | } | |
| 287 | } else { | |
| 288 | piecestr[0] = ' '; | |
| 289 | piecestr[1] = '\0'; | |
| 290 | } | |
| 291 | ||
| 292 | bool boardblack = (y&1)==(x&1); | |
| 293 | attrset((col==WHITE ? A_BOLD : A_DIM)| | |
| 294 | COLOR_PAIR(col == WHITE ? | |
| 295 | (boardblack ? COL_WB : COL_WW) : | |
| 296 | (boardblack ? COL_BB : COL_BW) | |
| 297 | ) | |
| 298 | ); | |
| 299 | ||
| 300 | int cy = perspective == WHITE ? boardy-y : boardy-7+y; | |
| 301 | int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3; | |
| 302 | mvprintw(cy, cx, " %s ", piecestr); | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | attrset(A_NORMAL); | |
| 307 | for (uint8_t i = 0 ; i < 8 ; i++) { | |
| 308 | int x = perspective == WHITE ? boardx+i*3+1 : boardx+22-i*3; | |
| 309 | int y = perspective == WHITE ? boardy-i : boardy-7+i; | |
| 310 | mvaddch(boardy+1, x, 'a'+i); | |
| 311 | mvaddch(y, boardx-2, '1'+i); | |
| 312 | } | |
| 313 | ||
| 314 | /* move log */ | |
| 315 | uint8_t logy = 2; | |
| 316 | const uint8_t logx = boardx + 28; | |
| 317 | move(logy, logx); | |
| 318 | ||
| 319 | /* count full moves */ | |
| 320 | unsigned int logi = 0; | |
| 321 | ||
| 322 | /* wrap log after 45 moves */ | |
| 323 | while (gamestate->movecount/6-logi/3 >= 15) { | |
| 324 | logi++; | |
| 325 | } | |
| 326 | ||
| 327 | for (unsigned mi = logi*2 ; mi < gamestate->movecount ; mi++) { | |
| 328 | bool iswhite = mi % 2 == 0; | |
| 329 | if (iswhite) { | |
| 330 | logi++; | |
| 331 | printw("%d. ", logi); | |
| 332 | } | |
| 333 | ||
| 334 | addstr(gamestate->moves[mi].string); | |
| 335 | if (!iswhite && logi%3 == 0) { | |
| 336 | move(++logy, logx); | |
| 337 | } else { | |
| 338 | addch(' '); | |
| 339 | } | |
| 340 | } | |
| 341 | } | |
| 342 | ||
| 343 | static void eval_move_failed_msg(int code) { | |
| 344 | switch (code) { | |
| 345 | case AMBIGUOUS_MOVE: | |
| 346 | printw("Ambiguous move - please specify the piece to move."); | |
| 347 | break; | |
| 348 | case INVALID_POSITION: | |
| 349 | printw("No piece can be moved this way."); | |
| 350 | break; | |
| 351 | case NEED_PROMOTION: | |
| 352 | printw("You need to promote the pawn (append \"=Q\" e.g.)!"); | |
| 353 | break; | |
| 354 | case KING_IN_CHECK: | |
| 355 | printw("Your king is in check!"); | |
| 356 | break; | |
| 357 | case PIECE_PINNED: | |
| 358 | printw("This piece is pinned!"); | |
| 359 | break; | |
| 360 | case INVALID_MOVE_SYNTAX: | |
| 361 | printw("Can't interpret move - please use algebraic notation."); | |
| 362 | break; | |
| 363 | case RULES_VIOLATED: | |
| 364 | printw("Move does not comply chess rules."); | |
| 365 | break; | |
| 366 | case KING_MOVES_INTO_CHECK: | |
| 367 | printw("Can't move the king into a check position."); | |
| 368 | break; | |
| 369 | default: | |
| 370 | printw("Unknown move parser error."); | |
| 371 | } | |
| 372 | } | |
| 373 | ||
| 374 | static void save_pgn(GameState *gamestate, GameInfo *gameinfo) { | |
| 375 | int y = getcury(stdscr); | |
| 376 | ||
| 377 | /* ask for player names */ | |
| 378 | { | |
| 379 | char pname[PLAYER_NAME_BUFLEN]; | |
| 380 | printw("\rWhite's name (%s): ", pgn_player_name(gamestate, WHITE)); | |
| 381 | clrtoeol(); | |
| 382 | if (getnstr(pname, PLAYER_NAME_BUFLEN) == OK && pname[0] != '\0') { | |
| 383 | strncpy(gamestate->wname, pname, PLAYER_NAME_BUFLEN); | |
| 384 | } | |
| 385 | move(y, 0); | |
| 386 | printw("\rBlack's name (%s): ", pgn_player_name(gamestate, BLACK)); | |
| 387 | clrtoeol(); | |
| 388 | if (getnstr(pname, PLAYER_NAME_BUFLEN) == OK && pname[0] != '\0') { | |
| 389 | strncpy(gamestate->bname, pname, PLAYER_NAME_BUFLEN); | |
| 390 | } | |
| 391 | move(y, 0); | |
| 392 | } | |
| 393 | ||
| 394 | bool export_comments = prompt_yesno("Export with comments"); | |
| 395 | ||
| 396 | printw("\rFilename: "); | |
| 397 | clrtoeol(); | |
| 398 | ||
| 399 | char filename[64]; | |
| 400 | if (getnstr(filename, 64) == OK && filename[0] != '\0') { | |
| 401 | move(y, 0); | |
| 402 | FILE *file = fopen(filename, "w"); | |
| 403 | if (file) { | |
| 404 | write_pgn(file, gamestate, gameinfo, export_comments); | |
| 405 | fclose(file); | |
| 406 | printw("File saved."); | |
| 407 | } else { | |
| 408 | printw("Can't write to file (%s).", strerror(errno)); | |
| 409 | } | |
| 410 | clrtoeol(); | |
| 411 | } | |
| 412 | } | |
| 413 | ||
| 414 | #define MOVESTR_BUFLEN 10 | |
| 415 | static int domove_singlemachine(GameState *gamestate, | |
| 416 | GameInfo *gameinfo, uint8_t curcolor) { | |
| 417 | ||
| 418 | ||
| 419 | size_t bufpos = 0; | |
| 420 | char movestr[MOVESTR_BUFLEN]; | |
| 421 | ||
| 422 | flushinp(); | |
| 423 | while (1) { | |
| 424 | const char *curcolorstr = curcolor == WHITE ? "White" : "Black"; | |
| 425 | if (timecontrol(gamestate, gameinfo)) { | |
| 426 | return 1; | |
| 427 | } | |
| 428 | move(inputy, 0); | |
| 429 | printw( | |
| 430 | "Use chess notation to enter your move.\n" | |
| 431 | "Or use a command: remis, resign, savepgn\n\n" | |
| 432 | "%s to move: ", curcolorstr); | |
| 433 | clrtoeol(); | |
| 434 | ||
| 435 | if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { | |
| 436 | if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { | |
| 437 | if (curcolor == WHITE) { | |
| 438 | gamestate->wresign = true; | |
| 439 | } else { | |
| 440 | gamestate->bresign = true; | |
| 441 | } | |
| 442 | return 1; | |
| 443 | } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { | |
| 444 | gamestate->remis = true; | |
| 445 | return 1; | |
| 446 | } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { | |
| 447 | save_pgn(gamestate, gameinfo); | |
| 448 | } else if (movestr[0] == 0) { | |
| 449 | /* ignore empty move strings and ask again */ | |
| 450 | } else { | |
| 451 | Move move; | |
| 452 | int result = eval_move(gamestate, movestr, &move, curcolor); | |
| 453 | if (result == VALID_MOVE_SYNTAX) { | |
| 454 | result = validate_move(gamestate, &move); | |
| 455 | if (result == VALID_MOVE_SEMANTICS) { | |
| 456 | apply_move(gamestate, &move); | |
| 457 | if (gamestate->checkmate) { | |
| 458 | return 1; | |
| 459 | } else if (gamestate->stalemate) { | |
| 460 | return 1; | |
| 461 | } else { | |
| 462 | return 0; | |
| 463 | } | |
| 464 | } else { | |
| 465 | eval_move_failed_msg(result); | |
| 466 | } | |
| 467 | } else { | |
| 468 | eval_move_failed_msg(result); | |
| 469 | } | |
| 470 | clrtoeol(); | |
| 471 | } | |
| 472 | } | |
| 473 | } | |
| 474 | } | |
| 475 | ||
| 476 | static int sendmove(GameState *gamestate, GameInfo *gameinfo, | |
| 477 | int opponent, uint8_t mycolor) { | |
| 478 | ||
| 479 | size_t bufpos = 0; | |
| 480 | char movestr[MOVESTR_BUFLEN]; | |
| 481 | bool remis_rejected = false; | |
| 482 | bool remis_suggested = false; | |
| 483 | bool resign_suggested = false; | |
| 484 | bool use_premove = false; | |
| 485 | uint8_t code; | |
| 486 | ||
| 487 | if (*gamestate->premove) { | |
| 488 | use_premove = true; | |
| 489 | const unsigned mlen = sizeof(gamestate->premove); | |
| 490 | strncpy(movestr, gamestate->premove, mlen); | |
| 491 | movestr[mlen] = '\0'; | |
| 492 | memset(gamestate->premove, 0, mlen); | |
| 493 | } | |
| 494 | ||
| 495 | flushinp(); | |
| 496 | while (1) { | |
| 497 | if (timecontrol(gamestate, gameinfo)) { | |
| 498 | net_send_code(opponent, NETCODE_TIMEOVER); | |
| 499 | return 1; | |
| 500 | } | |
| 501 | ||
| 502 | move(inputy, 0); | |
| 503 | printw("Use chess notation to enter your move.\n"); | |
| 504 | if (resign_suggested) { | |
| 505 | if (remis_suggested) { | |
| 506 | printw("The opponent asks you to resign or accept remis. \n\n"); | |
| 507 | } else { | |
| 508 | printw("The opponent asks you to resign. \n\n"); | |
| 509 | } | |
| 510 | } else if (remis_suggested) { | |
| 511 | printw("The opponent offers remis. Type remis to accept. \n\n"); | |
| 512 | } else if (remis_rejected) { | |
| 513 | printw("Remis offer rejected. \n\n"); | |
| 514 | } else { | |
| 515 | printw("Or use a command: remis, resign, savepgn \n\n"); | |
| 516 | } | |
| 517 | printw("Type your move: "); | |
| 518 | clrtoeol(); | |
| 519 | ||
| 520 | /* check if the opponent sent us something */ | |
| 521 | code = net_recieve_code_async(opponent); | |
| 522 | switch (code) { | |
| 523 | case NETCODE_REMIS: | |
| 524 | remis_suggested = true; | |
| 525 | break; | |
| 526 | case NETCODE_TAUNT: | |
| 527 | resign_suggested = true; | |
| 528 | break; | |
| 529 | case NETCODE_RESIGN: | |
| 530 | if (mycolor == WHITE) { | |
| 531 | gamestate->bresign = true; | |
| 532 | } else { | |
| 533 | gamestate->wresign = true; | |
| 534 | } | |
| 535 | return 1; | |
| 536 | case NETCODE_CONNLOST: | |
| 537 | gamestate->ragequit = true; | |
| 538 | return 1; | |
| 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("\nThe opponent sent an invalid network pacakge."); | |
| 548 | } | |
| 549 | ||
| 550 | /* read move */ | |
| 551 | if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { | |
| 552 | bool was_premove = use_premove; | |
| 553 | use_premove = false; | |
| 554 | if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { | |
| 555 | if (mycolor == WHITE) { | |
| 556 | gamestate->wresign = true; | |
| 557 | } else { | |
| 558 | gamestate->bresign = true; | |
| 559 | } | |
| 560 | net_send_code(opponent, NETCODE_RESIGN); | |
| 561 | return 1; | |
| 562 | } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { | |
| 563 | save_pgn(gamestate, gameinfo); | |
| 564 | } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { | |
| 565 | if (remis_suggested) { | |
| 566 | net_send_code(opponent, NETCODE_REMIS); | |
| 567 | gamestate->remis = true; | |
| 568 | return 1; | |
| 569 | } if (!remis_rejected) { | |
| 570 | net_send_code(opponent, NETCODE_REMIS); | |
| 571 | printw("Remis offer sent - waiting for acceptance..."); | |
| 572 | refresh(); | |
| 573 | code = net_recieve_code(opponent); | |
| 574 | if (code == NETCODE_ACCEPT) { | |
| 575 | gamestate->remis = true; | |
| 576 | return 1; | |
| 577 | } else if (code == NETCODE_CONNLOST) { | |
| 578 | gamestate->ragequit = true; | |
| 579 | return 1; | |
| 580 | } else { | |
| 581 | remis_rejected = true; | |
| 582 | } | |
| 583 | } | |
| 584 | } else if (movestr[0] == 0) { | |
| 585 | /* ignore empty move strings and ask again */ | |
| 586 | } else { | |
| 587 | Move move; | |
| 588 | int eval_result = eval_move(gamestate, movestr, &move, mycolor); | |
| 589 | switch (eval_result) { | |
| 590 | case VALID_MOVE_SYNTAX: | |
| 591 | net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move)); | |
| 592 | code = net_recieve_code(opponent); | |
| 593 | move.check = code == NETCODE_CHECK || | |
| 594 | code == NETCODE_CHECKMATE; | |
| 595 | gamestate->checkmate = code == NETCODE_CHECKMATE; | |
| 596 | gamestate->stalemate = code == NETCODE_STALEMATE; | |
| 597 | if (code == NETCODE_DECLINE) { | |
| 598 | uint32_t reason; | |
| 599 | net_recieve_data(opponent, &reason, sizeof(uint32_t)); | |
| 600 | reason = ntohl(reason); | |
| 601 | eval_move_failed_msg(reason); | |
| 602 | } else if (code == NETCODE_ACCEPT | |
| 603 | || code == NETCODE_CHECK | |
| 604 | || code == NETCODE_CHECKMATE | |
| 605 | || code == NETCODE_STALEMATE) { | |
| 606 | apply_move(gamestate, &move); | |
| 607 | if (gamestate->checkmate || gamestate->stalemate) { | |
| 608 | return 1; | |
| 609 | } else { | |
| 610 | return 0; | |
| 611 | } | |
| 612 | } else if (code == NETCODE_CONNLOST) { | |
| 613 | printw("Your opponent left the game."); | |
| 614 | return 1; | |
| 615 | } else { | |
| 616 | printw("Invalid network response."); | |
| 617 | } | |
| 618 | break; | |
| 619 | default: | |
| 620 | if (was_premove) { | |
| 621 | printw("\nThe prepared move could not be executed."); | |
| 622 | } else { | |
| 623 | eval_move_failed_msg(eval_result); | |
| 624 | } | |
| 625 | } | |
| 626 | clrtoeol(); | |
| 627 | } | |
| 628 | } | |
| 629 | } | |
| 630 | } | |
| 631 | ||
| 632 | static int recvmove(GameState *gamestate, GameInfo *gameinfo, | |
| 633 | int opponent, uint8_t mycolor) { | |
| 634 | memset(gamestate->premove, 0, sizeof(gamestate->premove)); | |
| 635 | ||
| 636 | size_t bufpos = 0; | |
| 637 | char movestr[MOVESTR_BUFLEN]; | |
| 638 | bool remis_suggested = false, resign_suggested = false; | |
| 639 | while (1) { | |
| 640 | timecontrol(gamestate, gameinfo); | |
| 641 | ||
| 642 | move(inputy, 0); | |
| 643 | printw("Waiting for opponent. Use chess notation to prepare a move.\n"); | |
| 644 | if (*gamestate->premove) { | |
| 645 | printw("Current pre-move: %s \n\n", | |
| 646 | gamestate->premove); | |
| 647 | } else if (remis_suggested && !resign_suggested) { | |
| 648 | printw("Suggested remis. \n\n"); | |
| 649 | } else if (resign_suggested) { | |
| 650 | if (remis_suggested) { | |
| 651 | printw("Suggested to resign or at least to accept remis. \n\n"); | |
| 652 | } else { | |
| 653 | printw("Suggested to resign. \n\n"); | |
| 654 | } | |
| 655 | } else { | |
| 656 | printw("Or use a command: remis, resign, taunt, savepgn \n\n"); | |
| 657 | } | |
| 658 | printw("Prepare your next move: "); | |
| 659 | clrtoeol(); | |
| 660 | refresh(); | |
| 661 | ||
| 662 | /* allow the player to prepare a move */ | |
| 663 | if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { | |
| 664 | if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { | |
| 665 | if (mycolor == WHITE) { | |
| 666 | gamestate->wresign = true; | |
| 667 | } else { | |
| 668 | gamestate->bresign = true; | |
| 669 | } | |
| 670 | net_send_code(opponent, NETCODE_RESIGN); | |
| 671 | return 1; | |
| 672 | } else if (strncmp(movestr, "taunt", MOVESTR_BUFLEN) == 0) { | |
| 673 | resign_suggested = true; | |
| 674 | net_send_code(opponent, NETCODE_TAUNT); | |
| 675 | } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { | |
| 676 | remis_suggested = true; | |
| 677 | net_send_code(opponent, NETCODE_REMIS); | |
| 678 | } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { | |
| 679 | save_pgn(gamestate, gameinfo); | |
| 680 | } else if (movestr[0] == 0) { | |
| 681 | memset(gamestate->premove, 0, sizeof(gamestate->premove)); | |
| 682 | } else { | |
| 683 | int res = check_move(movestr, mycolor); | |
| 684 | if (res == VALID_MOVE_SYNTAX) { | |
| 685 | strncpy(gamestate->premove, movestr, 8); | |
| 686 | memset(movestr, 0, MOVESTR_BUFLEN); | |
| 687 | bufpos = 0; | |
| 688 | clrtobot(); | |
| 689 | } else { | |
| 690 | eval_move_failed_msg(res); | |
| 691 | } | |
| 692 | } | |
| 693 | } | |
| 694 | ||
| 695 | /* read opponent's move */ | |
| 696 | uint8_t code = net_recieve_code_async(opponent); | |
| 697 | switch (code) { | |
| 698 | case NETCODE_TIMEOVER: | |
| 699 | /* redraw the time control */ | |
| 700 | timecontrol(gamestate, gameinfo); | |
| 701 | return 1; | |
| 702 | case NETCODE_RESIGN: | |
| 703 | if (mycolor == WHITE) { | |
| 704 | gamestate->bresign = true; | |
| 705 | } else { | |
| 706 | gamestate->wresign = true; | |
| 707 | } | |
| 708 | return 1; | |
| 709 | case NETCODE_CONNLOST: | |
| 710 | gamestate->ragequit = true; | |
| 711 | return 1; | |
| 712 | case NETCODE_REMIS: | |
| 713 | if (remis_suggested) { | |
| 714 | gamestate->remis = true; | |
| 715 | return 1; | |
| 716 | } else { | |
| 717 | if (prompt_yesno( | |
| 718 | "\rYour opponent offers remis - do you accept")) { | |
| 719 | gamestate->remis = true; | |
| 720 | net_send_code(opponent, NETCODE_ACCEPT); | |
| 721 | return 1; | |
| 722 | } else { | |
| 723 | net_send_code(opponent, NETCODE_DECLINE); | |
| 724 | } | |
| 725 | } | |
| 726 | break; | |
| 727 | case NETCODE_MOVE: { | |
| 728 | Move move; | |
| 729 | net_recieve_data(opponent, &move, sizeof(Move)); | |
| 730 | code = validate_move(gamestate, &move); | |
| 731 | if (code == VALID_MOVE_SEMANTICS) { | |
| 732 | apply_move(gamestate, &move); | |
| 733 | if (gamestate->checkmate) { | |
| 734 | net_send_code(opponent, NETCODE_CHECKMATE); | |
| 735 | return 1; | |
| 736 | } else if (gamestate->stalemate) { | |
| 737 | net_send_code(opponent, NETCODE_STALEMATE); | |
| 738 | return 1; | |
| 739 | } else if (move.check) { | |
| 740 | net_send_code(opponent, NETCODE_CHECK); | |
| 741 | } else { | |
| 742 | net_send_code(opponent, NETCODE_ACCEPT); | |
| 743 | } | |
| 744 | return 0; | |
| 745 | } else { | |
| 746 | uint32_t reason = htonl(code); | |
| 747 | net_send_data(opponent, NETCODE_DECLINE, | |
| 748 | &reason, sizeof(uint32_t)); | |
| 749 | } | |
| 750 | break; | |
| 751 | } | |
| 752 | case NETCODE_ERROR: | |
| 753 | printw("\rCannot perform asynchronous network IO"); | |
| 754 | cbreak(); getch(); | |
| 755 | exit(EXIT_FAILURE); | |
| 756 | case NETCODE_AGAIN: | |
| 757 | /* try again */ | |
| 758 | break; | |
| 759 | default: | |
| 760 | printw("\nInvalid network request."); | |
| 761 | } | |
| 762 | } | |
| 763 | } | |
| 764 | ||
| 765 | static void game_review(Settings* settings, GameState *gamestate) { | |
| 766 | const unsigned page_moves = 10; | |
| 767 | GameInfo *gameinfo = &(settings->gameinfo); | |
| 768 | GameState viewedstate = {0}; | |
| 769 | unsigned viewedmove = gamestate->movecount; | |
| 770 | bool redraw = true; | |
| 771 | ||
| 772 | noecho(); | |
| 773 | int c; | |
| 774 | do { | |
| 775 | if (redraw) { | |
| 776 | gamestate_cleanup(&viewedstate); | |
| 777 | gamestate_at_move(gamestate, viewedmove, &viewedstate); | |
| 778 | ||
| 779 | erase(); /* don't use clear() to avoid flickering */ | |
| 780 | draw_board(&viewedstate, WHITE, settings->unicode); | |
| 781 | timecontrol(&viewedstate, gameinfo); | |
| 782 | ||
| 783 | move(getmaxy(stdscr)-5, 0); | |
| 784 | if (gamestate->wresign) { | |
| 785 | addstr("White resigned.\n"); | |
| 786 | } else if (gamestate->bresign) { | |
| 787 | addstr("Black resigned.\n"); | |
| 788 | } else if (gamestate->remis) { | |
| 789 | addstr("The game ended remis.\n"); | |
| 790 | } else if (gamestate->stalemate) { | |
| 791 | addstr("The game ended in a stalemate.\n"); | |
| 792 | } else if (gamestate->checkmate) { | |
| 793 | printw("%s was checkmated.\n", | |
| 794 | gamestate->movecount % 2 == 0 ? "White" : "Black"); | |
| 795 | } else if (gamestate->ragequit) { | |
| 796 | printw("Your opponent disconnected.\n"); | |
| 797 | } | |
| 798 | addstr("\nPress 'q' to quit, 's' to save the position as PGN, or\n" | |
| 799 | "arrow keys, home/end, page up/down to review the game.\n"); | |
| 800 | flushinp(); | |
| 801 | redraw = false; | |
| 802 | } | |
| 803 | c = getch(); | |
| 804 | if (c == 's') { | |
| 805 | addch('\r'); | |
| 806 | echo(); | |
| 807 | save_pgn(&viewedstate, gameinfo); | |
| 808 | noecho(); | |
| 809 | redraw = true; | |
| 810 | } else if (c == KEY_UP || c == KEY_LEFT) { | |
| 811 | if (viewedmove > 0) { | |
| 812 | viewedmove--; | |
| 813 | redraw = true; | |
| 814 | } | |
| 815 | } else if (c == KEY_DOWN || c == KEY_RIGHT) { | |
| 816 | if (viewedmove < gamestate->movecount) { | |
| 817 | viewedmove++; | |
| 818 | redraw = true; | |
| 819 | } | |
| 820 | } else if (c == KEY_HOME) { | |
| 821 | viewedmove = 0; | |
| 822 | redraw = true; | |
| 823 | } else if (c == KEY_END) { | |
| 824 | viewedmove = gamestate->movecount; | |
| 825 | redraw = true; | |
| 826 | } else if (c == KEY_PPAGE) { | |
| 827 | if (viewedmove > page_moves) { | |
| 828 | viewedmove -= page_moves; | |
| 829 | } else { | |
| 830 | viewedmove = 0; | |
| 831 | } | |
| 832 | redraw = true; | |
| 833 | } else if (c == KEY_NPAGE) { | |
| 834 | viewedmove += page_moves; | |
| 835 | if (viewedmove > gamestate->movecount) { | |
| 836 | viewedmove = gamestate->movecount; | |
| 837 | } | |
| 838 | redraw = true; | |
| 839 | } | |
| 840 | } while (c != 'q'); | |
| 841 | echo(); | |
| 842 | gamestate_cleanup(&viewedstate); | |
| 843 | } | |
| 844 | ||
| 845 | static void game_play_singlemachine(Settings *settings) { | |
| 846 | inputy = getmaxy(stdscr) - 6; | |
| 847 | ||
| 848 | GameState gamestate; | |
| 849 | gamestate_init(&gamestate); | |
| 850 | uint8_t curcol = WHITE; | |
| 851 | ||
| 852 | if (settings->continuepgn) { | |
| 853 | FILE *pgnfile = fopen(settings->continuepgn, "r"); | |
| 854 | if (pgnfile) { | |
| 855 | int result = read_pgn(pgnfile, &gamestate, &(settings->gameinfo)); | |
| 856 | long position = ftell(pgnfile); | |
| 857 | fclose(pgnfile); | |
| 858 | if (result) { | |
| 859 | printw("Invalid PGN file content at position %ld:\n%s\n", | |
| 860 | position, pgn_error_str(result)); | |
| 861 | return; | |
| 862 | } | |
| 863 | if (!is_game_running(&gamestate)) { | |
| 864 | addstr("Game has ended. Use -S to analyze it.\n"); | |
| 865 | return; | |
| 866 | } | |
| 867 | curcol = opponent_color(last_move(&gamestate).piece&COLOR_MASK); | |
| 868 | } else { | |
| 869 | printw("Can't read PGN file (%s)\n", strerror(errno)); | |
| 870 | return; | |
| 871 | } | |
| 872 | } | |
| 873 | ||
| 874 | bool running; | |
| 875 | do { | |
| 876 | clear(); | |
| 877 | uint8_t perspective = settings->disableflip ? WHITE : curcol; | |
| 878 | draw_board(&gamestate, perspective, settings->unicode); | |
| 879 | running = !domove_singlemachine(&gamestate, | |
| 880 | &(settings->gameinfo), curcol); | |
| 881 | curcol = opponent_color(curcol); | |
| 882 | } while (running); | |
| 883 | ||
| 884 | game_review(settings, &gamestate); | |
| 885 | gamestate_cleanup(&gamestate); | |
| 886 | } | |
| 887 | ||
| 888 | static void game_play(Settings *settings, GameState *gamestate, int opponent) { | |
| 889 | inputy = getmaxy(stdscr) - 6; | |
| 890 | ||
| 891 | uint8_t mycolor = settings->gameinfo.servercolor; | |
| 892 | if (!settings->ishost) { | |
| 893 | mycolor = opponent_color(mycolor); | |
| 894 | } | |
| 895 | ||
| 896 | bool myturn = (gamestate->movecount > 0 ? | |
| 897 | (last_move(gamestate).piece & COLOR_MASK) : BLACK) != mycolor; | |
| 898 | ||
| 899 | bool running; | |
| 900 | do { | |
| 901 | clear(); | |
| 902 | draw_board(gamestate, mycolor, settings->unicode); | |
| 903 | if (myturn) { | |
| 904 | running = !sendmove(gamestate, &(settings->gameinfo), | |
| 905 | opponent, mycolor); | |
| 906 | } else { | |
| 907 | running = !recvmove(gamestate, &(settings->gameinfo), | |
| 908 | opponent, mycolor); | |
| 909 | } | |
| 910 | myturn ^= true; | |
| 911 | } while (running); | |
| 912 | } | |
| 913 | ||
| 914 | static void dump_gameinfo(GameInfo *gameinfo) { | |
| 915 | int serverwhite = gameinfo->servercolor == WHITE; | |
| 916 | attron(A_UNDERLINE); | |
| 917 | printw("Game details\n"); | |
| 918 | attroff(A_UNDERLINE); | |
| 919 | printw(" Server: %s\n Client: %s\n", | |
| 920 | serverwhite?"White":"Black", serverwhite?"Black":"White" | |
| 921 | ); | |
| 922 | if (gameinfo->timecontrol) { | |
| 923 | if (gameinfo->time % 60) { | |
| 924 | printw(" Time limit: %ds + %ds\n", | |
| 925 | gameinfo->time, gameinfo->addtime); | |
| 926 | } else { | |
| 927 | printw(" Time limit: %dm + %ds\n", | |
| 928 | gameinfo->time/60, gameinfo->addtime); | |
| 929 | } | |
| 930 | } else { | |
| 931 | printw(" No time limit\n"); | |
| 932 | } | |
| 933 | refresh(); | |
| 934 | } | |
| 935 | ||
| 936 | static void dump_moveinfo(GameState *gamestate) { | |
| 937 | for (unsigned i = 0 ; i < gamestate->movecount ; i++) { | |
| 938 | if (i % 2 == 0) { | |
| 939 | printw("%d. %s", 1+i/2, gamestate->moves[i].string); | |
| 940 | } else { | |
| 941 | printw("%s", gamestate->moves[i].string); | |
| 942 | } | |
| 943 | // only five moves reliably fit into one screen row | |
| 944 | if ((i+1) % 10) { | |
| 945 | addch(' '); | |
| 946 | } else { | |
| 947 | addch('\n'); | |
| 948 | } | |
| 949 | } | |
| 950 | refresh(); | |
| 951 | } | |
| 952 | ||
| 953 | static int server_fd = -1; | |
| 954 | static void interrupt_listen(int sig) { | |
| 955 | if (server_fd > -1) { | |
| 956 | // this interrupts | |
| 957 | close(server_fd); | |
| 958 | } | |
| 959 | } | |
| 960 | ||
| 961 | static int server_open(Server *server, Settings *settings) { | |
| 962 | printw("\nListening for client...\n"); | |
| 963 | refresh(); | |
| 964 | if (settings->usedomainsocket | |
| 965 | ? net_create_sock(server, settings->serverhost) | |
| 966 | : net_create_tcp(server, settings->port)) { | |
| 967 | addstr("Server creation failed"); | |
| 968 | return 1; | |
| 969 | } | |
| 970 | ||
| 971 | // allow Ctrl+C to interrupt the listening process | |
| 972 | server_fd = server->fd; | |
| 973 | signal(SIGINT, interrupt_listen); | |
| 974 | ||
| 975 | if (net_listen(server)) { | |
| 976 | addstr("Listening for client failed or interrupted"); | |
| 977 | return 1; | |
| 978 | } | |
| 979 | ||
| 980 | // restore default action | |
| 981 | signal(SIGINT, SIG_DFL); | |
| 982 | ||
| 983 | return 0; | |
| 984 | } | |
| 985 | ||
| 986 | static int server_handshake(Client *client) { | |
| 987 | net_send_code(client->fd, NETCODE_VERSION); | |
| 988 | if (net_recieve_code(client->fd) != NETCODE_VERSION) { | |
| 989 | addstr("Client uses an incompatible software version."); | |
| 990 | return 1; | |
| 991 | } | |
| 992 | ||
| 993 | addstr("Client connected - transmitting gameinfo..."); | |
| 994 | refresh(); | |
| 995 | ||
| 996 | return 0; | |
| 997 | } | |
| 998 | ||
| 999 | static int server_run(Settings *settings) { | |
| 1000 | Server server; | |
| 1001 | ||
| 1002 | dump_gameinfo(&(settings->gameinfo)); | |
| 1003 | GameState gamestate; | |
| 1004 | gamestate_init(&gamestate); | |
| 1005 | if (settings->continuepgn) { | |
| 1006 | /* preload PGN data before handshake */ | |
| 1007 | FILE *pgnfile = fopen(settings->continuepgn, "r"); | |
| 1008 | if (pgnfile) { | |
| 1009 | int result = read_pgn(pgnfile, &gamestate, | |
| 1010 | &(settings->gameinfo)); | |
| 1011 | long position = ftell(pgnfile); | |
| 1012 | fclose(pgnfile); | |
| 1013 | if (result) { | |
| 1014 | printw("Invalid PGN file content at position %ld:\n%s\n", | |
| 1015 | position, pgn_error_str(result)); | |
| 1016 | return 1; | |
| 1017 | } | |
| 1018 | if (!is_game_running(&gamestate)) { | |
| 1019 | addstr("Game has ended. Use -s to analyze it locally.\n"); | |
| 1020 | return 1; | |
| 1021 | } | |
| 1022 | addch('\n'); | |
| 1023 | dump_moveinfo(&gamestate); | |
| 1024 | addch('\n'); | |
| 1025 | } else { | |
| 1026 | printw("Can't read PGN file (%s)\n", strerror(errno)); | |
| 1027 | return 1; | |
| 1028 | } | |
| 1029 | } | |
| 1030 | ||
| 1031 | if (server_open(&server, settings)) { | |
| 1032 | net_destroy(&server); | |
| 1033 | return 1; | |
| 1034 | } | |
| 1035 | ||
| 1036 | if (server_handshake(server.client)) { | |
| 1037 | net_destroy(&server); | |
| 1038 | return 1; | |
| 1039 | } | |
| 1040 | ||
| 1041 | int fd = server.client->fd; | |
| 1042 | if (settings->continuepgn) { | |
| 1043 | /* Continue game, send PGN data */ | |
| 1044 | uint16_t mc = gamestate.movecount; | |
| 1045 | size_t pgndata_size = sizeof(GameInfo)+sizeof(mc)+mc*sizeof(Move); | |
| 1046 | char *pgndata = malloc(pgndata_size); | |
| 1047 | memcpy(pgndata, &(settings->gameinfo), sizeof(GameInfo)); | |
| 1048 | unsigned offset = sizeof(GameInfo); | |
| 1049 | memcpy(pgndata+offset, &mc, sizeof(mc)); | |
| 1050 | offset += sizeof(mc); | |
| 1051 | memcpy(pgndata+offset, gamestate.moves, mc*sizeof(Move)); | |
| 1052 | net_send_data(fd, NETCODE_PGNDATA, pgndata, pgndata_size); | |
| 1053 | free(pgndata); | |
| 1054 | } else { | |
| 1055 | /* Start new game */ | |
| 1056 | net_send_data(fd, NETCODE_GAMEINFO, | |
| 1057 | &(settings->gameinfo), sizeof(GameInfo)); | |
| 1058 | } | |
| 1059 | addstr("\rClient connected - awaiting challenge acceptance..."); | |
| 1060 | refresh(); | |
| 1061 | int code = net_recieve_code(fd); | |
| 1062 | int exitcode = 0; | |
| 1063 | if (code == NETCODE_ACCEPT) { | |
| 1064 | addstr("\rClient connected - challenge accepted."); | |
| 1065 | clrtoeol(); | |
| 1066 | game_play(settings, &gamestate, fd); | |
| 1067 | net_destroy(&server); | |
| 1068 | game_review(settings, &gamestate); | |
| 1069 | } else if (code == NETCODE_DECLINE) { | |
| 1070 | addstr("\rClient connected - challenge declined."); | |
| 1071 | clrtoeol(); | |
| 1072 | net_destroy(&server); | |
| 1073 | } else if (code == NETCODE_CONNLOST) { | |
| 1074 | addstr("\rClient connected - but gave no response."); | |
| 1075 | clrtoeol(); | |
| 1076 | net_destroy(&server); | |
| 1077 | } else { | |
| 1078 | addstr("\rInvalid client response"); | |
| 1079 | clrtoeol(); | |
| 1080 | ||
| 1081 | net_destroy(&server); | |
| 1082 | exitcode = 1; | |
| 1083 | } | |
| 1084 | gamestate_cleanup(&gamestate); | |
| 1085 | return exitcode; | |
| 1086 | } | |
| 1087 | ||
| 1088 | ||
| 1089 | static int client_connect(Server *server, Settings *settings) { | |
| 1090 | if (settings->usedomainsocket | |
| 1091 | ? net_find_sock(server, settings->serverhost) | |
| 1092 | : net_find_tcp(server, settings->serverhost, settings->port)) { | |
| 1093 | addstr("Can't find server"); | |
| 1094 | return 1; | |
| 1095 | } | |
| 1096 | ||
| 1097 | if (net_connect(server)) { | |
| 1098 | addstr("Can't connect to server"); | |
| 1099 | return 1; | |
| 1100 | } | |
| 1101 | ||
| 1102 | return 0; | |
| 1103 | } | |
| 1104 | ||
| 1105 | static int client_handshake(Server *server) { | |
| 1106 | if (net_recieve_code(server->fd) != NETCODE_VERSION) { | |
| 1107 | addstr("Server uses an incompatible software version."); | |
| 1108 | return 1; | |
| 1109 | } else { | |
| 1110 | net_send_code(server->fd, NETCODE_VERSION); | |
| 1111 | } | |
| 1112 | ||
| 1113 | printw("Connection established!\n\n"); | |
| 1114 | refresh(); | |
| 1115 | ||
| 1116 | return 0; | |
| 1117 | } | |
| 1118 | ||
| 1119 | static int client_run(Settings *settings) { | |
| 1120 | Server server; | |
| 1121 | ||
| 1122 | if (client_connect(&server, settings)) { | |
| 1123 | net_destroy(&server); | |
| 1124 | return 1; | |
| 1125 | } | |
| 1126 | ||
| 1127 | if (client_handshake(&server)) { | |
| 1128 | net_destroy(&server); | |
| 1129 | return 1; | |
| 1130 | } | |
| 1131 | ||
| 1132 | uint8_t code = net_recieve_code(server.fd); | |
| 1133 | GameState gamestate; | |
| 1134 | gamestate_init(&gamestate); | |
| 1135 | bool played = false; | |
| 1136 | if (code == NETCODE_GAMEINFO) { | |
| 1137 | /* Start new game */ | |
| 1138 | net_recieve_data(server.fd, &(settings->gameinfo), sizeof(GameInfo)); | |
| 1139 | dump_gameinfo(&(settings->gameinfo)); | |
| 1140 | if (prompt_yesno("Accept challenge")) { | |
| 1141 | net_send_code(server.fd, NETCODE_ACCEPT); | |
| 1142 | game_play(settings, &gamestate, server.fd); | |
| 1143 | played = true; | |
| 1144 | } else { | |
| 1145 | net_send_code(server.fd, NETCODE_DECLINE); | |
| 1146 | } | |
| 1147 | } else if (code == NETCODE_PGNDATA) { | |
| 1148 | net_recieve_data(server.fd, &(settings->gameinfo), sizeof(GameInfo)); | |
| 1149 | dump_gameinfo(&(settings->gameinfo)); | |
| 1150 | uint16_t mc; | |
| 1151 | net_recieve_data(server.fd, &mc, sizeof(mc)); | |
| 1152 | Move *moves = calloc(mc, sizeof(Move)); | |
| 1153 | net_recieve_data(server.fd, moves, mc*sizeof(Move)); | |
| 1154 | for (size_t i = 0 ; i < mc ; i++) { | |
| 1155 | apply_move(&gamestate, &(moves[i])); | |
| 1156 | } | |
| 1157 | free(moves); | |
| 1158 | addch('\n'); | |
| 1159 | dump_moveinfo(&gamestate); | |
| 1160 | if (prompt_yesno( | |
| 1161 | "\n\nServer wants to continue a game. Accept challenge")) { | |
| 1162 | net_send_code(server.fd, NETCODE_ACCEPT); | |
| 1163 | game_play(settings, &gamestate, server.fd); | |
| 1164 | played = true; | |
| 1165 | } else { | |
| 1166 | net_send_code(server.fd, NETCODE_DECLINE); | |
| 1167 | } | |
| 1168 | } else { | |
| 1169 | addstr("Server sent invalid gameinfo."); | |
| 1170 | net_destroy(&server); | |
| 1171 | return 1; | |
| 1172 | } | |
| 1173 | ||
| 1174 | if (played) { | |
| 1175 | game_review(settings, &gamestate); | |
| 1176 | } | |
| 1177 | gamestate_cleanup(&gamestate); | |
| 1178 | ||
| 1179 | net_destroy(&server); | |
| 1180 | return 0; | |
| 1181 | } | |
| 1182 | ||
| 0 | 1183 | int main(int argc, char **argv) { |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
1184 | srand(time(NULL)); |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
1185 | |
|
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
1186 | init_settings(); |
|
2
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
1187 | if (get_settings(argc, argv, &settings)) { |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
1188 | return 1; |
|
0a08f79c320d
fixed network code + added game info and transmission of game info
Mike Becker <universe@uap-core.de>
parents:
1
diff
changeset
|
1189 | } |
| 52 | 1190 | |
|
23
824c9522ce66
introduced game state structure
Mike Becker <universe@uap-core.de>
parents:
16
diff
changeset
|
1191 | initscr(); |
|
34
c4d4b8a8f902
added nonblocking read for network games + minor build system fixes
Mike Becker <universe@uap-core.de>
parents:
33
diff
changeset
|
1192 | halfdelay(1); |
|
80
b980a7192b5a
replace _Bool with bool from C23 and/or stdbool.h
Mike Becker <universe@uap-core.de>
parents:
78
diff
changeset
|
1193 | keypad(stdscr, true); |
|
7
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1194 | if (has_colors()) { |
|
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1195 | start_color(); |
|
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1196 | init_colorpairs(); |
| 35 | 1197 | bkgd(COLOR_PAIR(COL_APP)); |
|
7
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1198 | } else { |
|
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1199 | fprintf(stderr, "Non-colored terminals are not supported yet."); |
|
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1200 | endwin(); |
|
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1201 | return EXIT_FAILURE; |
|
41468077b5bb
implemented chess board and remis/surrender messages
Mike Becker <universe@uap-core.de>
parents:
5
diff
changeset
|
1202 | } |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
1203 | atexit(cleanup); |
|
3
3693fd2eb0e9
changed UI to ncurses session + added network handshake
Mike Becker <universe@uap-core.de>
parents:
2
diff
changeset
|
1204 | |
|
46
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1205 | int exitcode; |
|
26
e0a76ee1bb2b
introduced single machine mode
Mike Becker <universe@uap-core.de>
parents:
23
diff
changeset
|
1206 | if (settings.singlemachine) { |
|
83
ead00609e5e6
fix that network connection is still open during game review
Mike Becker <universe@uap-core.de>
parents:
80
diff
changeset
|
1207 | game_play_singlemachine(&settings); |
|
46
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1208 | exitcode = EXIT_SUCCESS; |
|
26
e0a76ee1bb2b
introduced single machine mode
Mike Becker <universe@uap-core.de>
parents:
23
diff
changeset
|
1209 | } else { |
|
94
864f59271974
implement option to play via Unix domain socket
Mike Becker <universe@uap-core.de>
parents:
92
diff
changeset
|
1210 | exitcode = settings.ishost ? |
|
26
e0a76ee1bb2b
introduced single machine mode
Mike Becker <universe@uap-core.de>
parents:
23
diff
changeset
|
1211 | server_run(&settings) : client_run(&settings); |
|
e0a76ee1bb2b
introduced single machine mode
Mike Becker <universe@uap-core.de>
parents:
23
diff
changeset
|
1212 | } |
|
46
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1213 | |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1214 | mvaddstr(getmaxy(stdscr)-1, 0, |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1215 | "Game has ended. Press any key to leave..."); |
|
50
41017d0a72c5
added pgn parser and writer (without comment support yet) + minor refactorings
Mike Becker <universe@uap-core.de>
parents:
47
diff
changeset
|
1216 | clrtoeol(); |
|
46
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1217 | refresh(); |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1218 | cbreak(); |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1219 | flushinp(); |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1220 | getch(); |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1221 | |
|
4dcfb4c58b6d
netcode is now aware of connection losses
Mike Becker <universe@uap-core.de>
parents:
35
diff
changeset
|
1222 | return exitcode; |
| 0 | 1223 | } |
| 1224 |