[go: up one dir, main page]

Menu

[190a4b]: / lib / strat.c  Maximize  Restore  History

Download this file

289 lines (256 with data), 8.7 kB

  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
/**
* @file
* Code to read files of strategic moves and put them in a
* simpler form intended to be sorted.
* Last Modified: Sat Dec 27 09:17:15 PST 2014
* @author Kevin O'Gorman
**/
/*
* Copyright 2012-2014 Kevin O'Gorman <kogorman@gmail.com>.
* Distributed under the GNU General Public License.
*
* This file is part of libqubist, the library of functions for playing
* 4x4x4 tic-tac-toe, also known as Qubic, and by some other names
* outside the USA.
*
* Libqubist is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2.
*
* Libqubist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* @file
* Code to read files of strategic moves and put them in a
* simpler form intended to be sorted. The input must contain the moves, one per line,
* after a line that begins with 7 hyphens. There must be nothing else after this line.
*
* NOTE: Oren's strategic moves are saved on Google Docs at
* https://drive.google.com/file/d/0B6pbHEZND52eMzlzWWRCWFdrTm8/edit?usp=sharing
* You just place it in the current directory, and Qubist will read it in when it starts.
* You can also develop your own file, but it has to have the name "qubic.dictionary" for
* it to be accepted.
*
* Oren's file describes its own format. This code also accepts "-" characters as explicit
* empty cells, which is more convenient in these days of cheap storage.
*
* You can check you have the right file with any of these checksums:
* <pre>
* MD5SUM: 9b456c0256ad962ba767872cae426da1
* sha1sum: f166e5f03747692f2dfca3d938497e2631c74289
* sha224sum: 59ea2fc9de3e7c5f3fd33f62a269cb37c1ddd055fc7782324faf5941
* sha256sum: 38d9b7820fd97269968e6eea32c6448712d0551e66c1f2ed1ce47f74f3069499
* sha384sum: d601ac40226c2896ac6348dadf8c40f6c0c551a68f572bc8f320d63c616050f0384c62f70916e4af45f379671603d52
* sha512sum: cfe3ffc274a391754e35042f688de2d4d52e2588c965bb73087ac90d2bb1ece3a886b3c635b9d45257618861e702abf231a71f1624b361d3e8dedf005a68bf93
* </pre>
*
* TODO: accept and process multiple strategic moves per position, possibly with depth-of-win
* information.
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#undef NDEBUG
#define NDEBUG
#include <assert.h>
#define BUFL 1024
#include "moves.h"
/* Private data */
static move_t *qs_allstrats = NULL; /**< array of all strategic moves */
static int qs_strats_allocated = 0; /**< used-count within qs_allstrats */
static int qs_strats_count = 0; /**< allocated size of qs_allstrats */
static int qs_id = 0; /**< assigns sequential IDs to strategic moves */
static move_t *qs_strat_census_tabs[65] = {NULL}; /**< pointers (tabs) to census-groups in qs_allstrats */
/* Getter function. Also avoids compiler quibbles about array length. */
move_t *
qb_get_stratpos(int i)
{
return qs_strat_census_tabs[i];
}
/* Getter function. Also avoids compiler quibbles about array length. */
static int qs_max_strat_census = -1; /**< the current maximum census */
int
qb_get_max_census(void)
{
return qs_max_strat_census;
}
/* getter function. Also avoids compiler quibbles about array length. */
int
qb_get_strat_count(int census)
{
assert(0 <= census && census <= 64);
if (census > qs_max_strat_census) return 0;
return qs_strat_census_tabs[census+1] - qs_strat_census_tabs[census];
}
#define STRAT_CHUNK 3000 /**< How the collection grows */
/**
* Ensure adequate space in qs_allstrats.
*/
static void
makespace(void)
{
if (qs_strats_count + 1 >= qs_strats_allocated) {
qs_strats_allocated += STRAT_CHUNK;
qs_allstrats = (move_t*)realloc(qs_allstrats, qs_strats_allocated * sizeof(move_t));
if (!qs_allstrats) {
perror("realloc");
exit(EXIT_FAILURE);
}
}
}
/**
* Read strategic positions and set up the global arrays and variables.
*/
static void
qs_readStrats(FILE *stream) {
char buf[BUFL];
char *endptr;
/* Read and ignore everything up to a line that begins with at least 7 hypens. */
while (fgets(buf, BUFL, stream) != NULL) {
if (strncmp(buf, "-------", 7) == 0) {
break;
}
}
if (feof(stream) || ferror(stream)) {
fprintf(stderr, " *** ERROR 1: error reading strategic moves.\n");
exit(EXIT_FAILURE);
}
/* read actual strategic positions until the end of file */
while (fgets(buf, BUFL, stream) != NULL) {
int wt = 0;
int cur = 0;
int pos = 0;
int n;
char ch;
makespace();
/* read the position part */
while ((ch = buf[cur++]) != ' ' && isprint(ch)) {
/* always looking at an x, an o, - */
qs_allstrats[qs_strats_count].posn[pos++] = ch;
if (ch == 'x' || ch == 'o') wt++;
/* scan a number if present, making sure not to treat a '-' as beginning a number */
if (isdigit(buf[cur])) {
n = strtol(buf + cur, &endptr, 10);
cur = endptr - buf;
while (n) {
qs_allstrats[qs_strats_count].posn[pos++] = '-';
n--;
}
}
}
if (ch != ' ') {
fprintf(stderr, " *** ERROR 2: error reading strategic moves.\n");
exit(EXIT_FAILURE);
}
while (pos < 64) {
qs_allstrats[qs_strats_count].posn[pos++] = '-';
}
qs_allstrats[qs_strats_count].posn[64] = '\0';
qs_allstrats[qs_strats_count].census = wt;
qs_allstrats[qs_strats_count].move = strtol(buf + cur, &endptr, 10);
qs_allstrats[qs_strats_count].id = ++qs_id;
qs_strats_count++;
}
if (ferror(stream)) {
fprintf(stderr, " *** ERROR 4: error reading strategic moves.\n");
exit(EXIT_FAILURE);
}
}
/**
* Comparison routine for use with qsort.
*/
int
compare_strats(const void *a, const void *b)
{
int diff;
diff = ((move_t*)a)->census - ((move_t*)b)->census;
if (diff) return diff;
return strncmp(((move_t*)a)->posn, ((move_t*)b)->posn, 64);
}
/**
* Finalize the reading of strategic positions, sorting by census then position,
* then using realloc() to discard any unused positions.
*/
static void
qs_finalize_strats(void)
{
int i;
int c;
if (qs_strats_allocated > 0) {
/* finalize, sort and resize the qs_allstrats list of strategic moves */
qs_allstrats[qs_strats_count].census = 65;
qs_allstrats[qs_strats_count].id = 999999999;
qs_strats_count++;
qs_strats_allocated = qs_strats_count;
qsort(qs_allstrats, qs_strats_count, sizeof(move_t), (int (*)(const void*,const void*))compare_strats);
qs_allstrats = (move_t*)realloc(qs_allstrats, qs_strats_allocated * sizeof(move_t));
qs_max_strat_census = qs_allstrats[qs_strats_count-2].census;
/* Create tabs into the list, organized by census */
qs_strat_census_tabs[0] = qs_allstrats;
for (c = 0, i = 1; i < qs_strats_count; i++) {
while (c != qs_allstrats[i].census) {
assert(c < qs_allstrats[i].census);
c++;
qs_strat_census_tabs[c] = qs_allstrats + i;
}
}
}
}
/**
* Set up the table of strategic positions and moves. This comes from all of the
* files with names ending in .dictionary.
*/
void
qb_initialize_strats(void)
{
DIR *dir;
FILE *stream;
struct dirent* entry;
int len;
dir = opendir(".");
if (!dir) {
perror("opendir");
exit(EXIT_FAILURE);
}
while ((entry = readdir(dir))) {
len = strlen(entry->d_name);
if (len > 11 && !strcmp(".dictionary", entry->d_name + len - 11)) {
stream = fopen(entry->d_name, "r");
qs_readStrats(stream);
fclose(stream);
}
}
closedir(dir);
qs_finalize_strats();
}
#if 0 /* For unit testing only */
/**
* The usual main function. In this case it's all of the compile-moves program.
* @param argc a count of the fields on the command-line.
* @param argv an array of pointers to those fields.
* @return 0 for success; any result in the range 1-255 on failure.
*/
int
main(int argc, char *argv[])
{
int i;
qb_initialize_strats();
for (i = 0; i < 32; i++) {
printf("%2d: %d\n", i, qb_get_strat_count(i));
}
printf("Max census of any strategic move: %d\n", qb_get_max_census());
return EXIT_SUCCESS;
}
#endif
/* vim: set expandtab autoindent shiftround nosmartindent tabstop=8 softtabstop=2 shiftwidth=2: */