WPBDC 2013.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/ext/WPBDC/judge.c ADDED
@@ -0,0 +1,563 @@
1
+ /*
2
+ *
3
+ * judge.c -- Ruby Extension WPBDC. Support for the West Point
4
+ * Bridge Design Contest web site. Ruby-visible constants
5
+ * and functions.
6
+ *
7
+ * Hashing code assumes unsigned int is 32 bits, but should otherwise be portable.
8
+ *
9
+ * This compiles judges for V3, V4 and V4 (contest) of the WPBD
10
+ * according to compile time constants.
11
+ * The corresponding values of BD_VERSION are:
12
+ * V3 => 0x300
13
+ * V4 => 0x400
14
+ * V4 (2002 contest) => 0x402c
15
+ * V4 (2003 contest) => 0x403c
16
+ * V4 (2004 contest) => 0x404c
17
+ * etc.
18
+ */
19
+
20
+ #include "stdafx.h"
21
+
22
+ // Constants and data structures Ruby does not need to know about.
23
+ #include "internal.h"
24
+
25
+ // External interface.
26
+ #include "judge.h"
27
+
28
+ #if BD_VERSION != 0x412c
29
+ #error Wrong BD_VERSION. Should be 0x412c.
30
+ #endif
31
+
32
+ // Extension stubs --------------------------------------------------------------
33
+
34
+ // These are the routines called by the Ruby interface code.
35
+
36
+ void endecrypt(STRING *bridge_as_string)
37
+ {
38
+ endecrypt_in_place(bridge_as_string->ptr, bridge_as_string->size);
39
+ }
40
+
41
+ // Parse bridge as string.
42
+ // Return a result struct that describes what happened.
43
+ void analyze(STRING *bridge_as_string, struct analysis_result_t *result)
44
+ {
45
+ do_analyze(bridge_as_string, 0, 0, 0, 0, 0, result);
46
+ }
47
+
48
+ int compare(STRING *bridge_as_string_a, STRING *bridge_as_string_b)
49
+ {
50
+ TBridge a[1], canonical_a[1], b[1], canonical_b[1];
51
+ int rtn;
52
+
53
+ init_bridge(a);
54
+ init_bridge(canonical_a);
55
+ init_bridge(b);
56
+ init_bridge(canonical_b);
57
+
58
+ parse_bridge(a, bridge_as_string_a);
59
+ if (a->error != BridgeNoError)
60
+ return -1;
61
+ parse_bridge(b, bridge_as_string_b);
62
+ if (b->error != BridgeNoError)
63
+ return -1;
64
+ canonicalize(canonical_a, a);
65
+ if (canonical_a->error != BridgeNoError)
66
+ return -1;
67
+ canonicalize(canonical_b, b);
68
+ if (canonical_b->error != BridgeNoError)
69
+ return -1;
70
+ rtn = bridges_indentical(canonical_a, canonical_b);
71
+
72
+ clear_bridge(a);
73
+ clear_bridge(canonical_a);
74
+ clear_bridge(b);
75
+ clear_bridge(canonical_b);
76
+
77
+ return rtn;
78
+ }
79
+
80
+ char *variant(STRING *bridge_as_string, int seed)
81
+ {
82
+ char *rtn;
83
+ TBridge bridge[1], variant_bridge[1];
84
+
85
+ init_bridge(bridge);
86
+ init_bridge(variant_bridge);
87
+
88
+ parse_bridge(bridge, bridge_as_string);
89
+
90
+ if (bridge->error == BridgeNoError) {
91
+ vary(variant_bridge, bridge, seed);
92
+ rtn = unparse_bridge(variant_bridge);
93
+ }
94
+ else {
95
+ rtn = 0;
96
+ }
97
+ clear_bridge(bridge);
98
+ clear_bridge(variant_bridge);
99
+
100
+ return rtn;
101
+ }
102
+
103
+ char *failed_variant(STRING *bridge_as_string, int seed)
104
+ {
105
+ char *rtn;
106
+ TBridge bridge[1], variant_bridge[1];
107
+
108
+ init_bridge(bridge);
109
+ init_bridge(variant_bridge);
110
+
111
+ parse_bridge(bridge, bridge_as_string);
112
+
113
+ if (bridge->error == BridgeNoError) {
114
+ induce_failure(variant_bridge, bridge, seed);
115
+ rtn = unparse_bridge(variant_bridge);
116
+ }
117
+ else {
118
+ rtn = 0;
119
+ }
120
+ clear_bridge(bridge);
121
+ clear_bridge(variant_bridge);
122
+
123
+ return rtn;
124
+ }
125
+
126
+ char *perturbation(STRING *bridge_as_string, int seed,
127
+ int n_joints, int n_members)
128
+ {
129
+ char *rtn;
130
+ TBridge bridge[1], perturbed_bridge[1];
131
+
132
+ init_bridge(bridge);
133
+ init_bridge(perturbed_bridge);
134
+
135
+ parse_bridge(bridge, bridge_as_string);
136
+
137
+ if (bridge->error == BridgeNoError) {
138
+ perturb(perturbed_bridge, bridge, seed, n_joints, n_members);
139
+ rtn = unparse_bridge(perturbed_bridge);
140
+ }
141
+ else {
142
+ rtn = 0;
143
+ }
144
+ clear_bridge(bridge);
145
+ clear_bridge(perturbed_bridge);
146
+
147
+ return rtn;
148
+ }
149
+
150
+
151
+ void sketch(STRING *bridge_as_string,
152
+ int width, int height,
153
+ COMPRESSED_IMAGE *compressed_image,
154
+ struct analysis_result_t *result)
155
+ {
156
+ TBridge bridge[1];
157
+ TAnalysis analysis[1];
158
+ struct analysis_result_t local_result[1];
159
+
160
+ if (!result)
161
+ result = local_result;
162
+
163
+ do_analyze(bridge_as_string, analysis, bridge, 0, 0, 0, result);
164
+
165
+ if (result->status == BRIDGE_OK)
166
+ do_sketch(bridge, analysis, width, height, compressed_image);
167
+
168
+ clear_bridge(bridge);
169
+ clear_analysis(analysis);
170
+ }
171
+
172
+ char *get_local_contest_number(STRING *number_as_string)
173
+ {
174
+ char *rtn;
175
+
176
+ if (number_as_string->size != SCENARIO_NUMBER_SIZE)
177
+ return NULL;
178
+ return local_contest_number_to_id(number_as_string->ptr);
179
+ }
180
+
181
+ char *analysis_table(STRING *bridge_as_string)
182
+ {
183
+ TBridge bridge[1];
184
+ TParams params[1];
185
+ TGeometry geometry[1];
186
+ TLoading loading[1];
187
+ TAnalysis analysis[1];
188
+ struct analysis_result_t result[1];
189
+ char *rtn = 0;
190
+
191
+ do_analyze(bridge_as_string, analysis, bridge, geometry, loading, params, result);
192
+ if (!bridge->error)
193
+ rtn = analysis_to_html(analysis, bridge, geometry, loading, params, result);
194
+
195
+ clear_analysis(analysis);
196
+ clear_bridge(bridge);
197
+ clear_geometry(geometry);
198
+ clear_loading(loading);
199
+ clear_params(params);
200
+
201
+ return rtn;
202
+ }
203
+
204
+ #if defined(NATIVE_TEST) && !defined(CPROTO)
205
+
206
+ //#define WRITE_EXAMPLES
207
+
208
+ #ifdef WRITE_EXAMPLES
209
+ #define EG_DIR "../Eg/2011"
210
+ #define NEW_EG_DIR "../Eg/2012"
211
+ #else
212
+ #define EG_DIR "../Eg/2012"
213
+ #endif
214
+
215
+ #include <io.h>
216
+ #include <crtdbg.h>
217
+ #include <sys/types.h>
218
+ #include <sys/stat.h>
219
+
220
+ void split_fn(char *fn, char *path, char *name, char *ext)
221
+ {
222
+ char buf[256];
223
+ char *p, *pdot, *pslash;
224
+
225
+ strcpy(buf, fn);
226
+
227
+ pdot = strrchr(buf, '.');
228
+ if (pdot) {
229
+ strcpy(ext, pdot);
230
+ *pdot = '\0';
231
+ }
232
+ else
233
+ ext[0] = '\0';
234
+
235
+ pslash = strrchr(buf, '/');
236
+ if ((p = strrchr(buf, '\\')) != NULL && (!pslash || p > pslash))
237
+ pslash = p;
238
+ if (pslash) {
239
+ strcpy(name, pslash + 1);
240
+ *(pslash + 1) = '\0';
241
+ strcpy(path, buf);
242
+ }
243
+ else {
244
+ strcpy(name, buf);
245
+ path[0] = '\0';
246
+ }
247
+ }
248
+
249
+ int main(int argc, char* argv[])
250
+ {
251
+ extern unsigned contest_year; // ugly hack go read other years' bridge files
252
+
253
+ FILE *f;
254
+ char fname[256];
255
+ char raw[4*1024];
256
+ STRING bridge_str[1], copy_str[1];
257
+ char raw_hash[HASH_SIZE];
258
+ char variant_hash[HASH_SIZE];
259
+ char buf[256], path[256], name[256], ext[256];
260
+ char *text;
261
+ struct analysis_result_t result[1];
262
+
263
+ TBridge bridge[1];
264
+ TBridge copy[1];
265
+ TGeometry geometry[1];
266
+ TParams params[1];
267
+ TLoading loading[1];
268
+ TAnalysis anal[1];
269
+ int i, n, test_case;
270
+ long h;
271
+ struct _finddata_t file_data[1];
272
+ COMPRESSED_IMAGE compressed_image[1];
273
+
274
+ #ifdef WRITE_EXAMPLES
275
+ int n_examples_read = 0;
276
+ int n_examples_written = 0;
277
+ #endif
278
+
279
+ #ifdef _CRTDBG_MAP_ALLOC
280
+ {
281
+ int mem_debug_flags;
282
+
283
+ mem_debug_flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
284
+ mem_debug_flags |= _CRTDBG_LEAK_CHECK_DF;
285
+ _CrtSetDbgFlag(mem_debug_flags);
286
+ // Leak memory to show we're running.
287
+ malloc(1);
288
+ }
289
+ #endif
290
+
291
+ init_bridge(bridge);
292
+ init_bridge(copy);
293
+ init_geometry(geometry);
294
+ init_params(params);
295
+ init_loading(loading);
296
+ init_analysis(anal);
297
+ init_compressed_image(compressed_image);
298
+
299
+ setup_params(params);
300
+
301
+ if (test_scenario_table() != 0)
302
+ return 1;
303
+
304
+ for (test_case = 1;;test_case++) {
305
+
306
+ if (test_case == 1) {
307
+ h = _findfirst( EG_DIR "/*.bdc", file_data );
308
+ if (h == -1) {
309
+ printf( "No bridge files!\n" );
310
+ return 17;
311
+ }
312
+ }
313
+ else {
314
+ if (_findnext(h, file_data) != 0) {
315
+ _findclose(h);
316
+ test_case--;
317
+ break;
318
+ }
319
+ }
320
+ if (strcmp(file_data->name, "A-arch-10-4-1.bdc") != 0)
321
+ continue;
322
+
323
+ printf("Test case %d (%s):\n", test_case, file_data->name);
324
+ sprintf(fname, EG_DIR "/%s", file_data->name);
325
+ f = fopen(fname, "rb");
326
+ if (!f) {
327
+ printf("can't open bridge file\n");
328
+ return 1;
329
+ }
330
+ n = fread(raw, 1, sizeof raw, f);
331
+ raw[n] = '\0';
332
+ fclose(f);
333
+
334
+ bridge_str->size = n;
335
+ bridge_str->ptr = raw;
336
+ endecrypt(bridge_str);
337
+
338
+ printf("decrypted bridge:\n");
339
+ for (i = 0; i < n; i++)
340
+ putchar(bridge_str->ptr[i]);
341
+ printf("\n");
342
+
343
+ #ifdef WRITE_EXAMPLES
344
+
345
+ // Set the parser to look for last year's bridges.
346
+ contest_year = CONTEST_YEAR - 1;
347
+ #endif
348
+
349
+ // Try a parse.
350
+ parse_bridge(bridge, bridge_str);
351
+
352
+
353
+ // Back to this year.
354
+ contest_year = CONTEST_YEAR;
355
+
356
+ if (bridge->error != BridgeNoError) {
357
+ printf("parse error %d, line %d in %s; skipping...\n", bridge->error, bridge->error_line, fname);
358
+ continue;
359
+ }
360
+ printf("scenario id %s\n", bridge->scenario_descriptor.id);
361
+
362
+ #ifdef WRITE_EXAMPLES
363
+
364
+ if (!strstr(file_data->name, "failed")) {
365
+
366
+ struct _stat stat_buf[1];
367
+
368
+ n_examples_read++;
369
+
370
+ split_fn(file_data->name, path, name, ext);
371
+
372
+ n = fix_failure(copy, bridge, params);
373
+
374
+ if (n >= 0) {
375
+ printf("writing fixed version\n");
376
+ sprintf(fname, NEW_EG_DIR "/" "%s.bdc", name);
377
+ text = unparse_bridge(copy);
378
+ copy_str->ptr = text;
379
+ copy_str->size = strlen(text);
380
+ endecrypt(copy_str); // really encrypt
381
+ f = fopen(fname, "wb");
382
+ if (!f) {
383
+ printf("can't open fixed bridge file %s for writing\n", fname);
384
+ return 1;
385
+ }
386
+ fwrite(text, copy_str->size, 1, f);
387
+ fclose(f);
388
+ n_examples_written++;
389
+ }
390
+ else {
391
+ printf("attempt to fix bridge %s failed\n", fname);
392
+ }
393
+
394
+ // Write a failed version of this bridge for future testing.
395
+ printf("writing failed version\n");
396
+ sprintf(fname, NEW_EG_DIR "/" "%s-failed%s", name, ext);
397
+ if (_stat(fname, stat_buf) != 0 && induce_failure(copy, bridge, 0) == 0) {
398
+ text = unparse_bridge(copy);
399
+ copy_str->ptr = text;
400
+ copy_str->size = strlen(text);
401
+ endecrypt(copy_str); // really encrypt
402
+ f = fopen(fname, "wb");
403
+ if (!f) {
404
+ printf("can't open failed bridge file for writing\n");
405
+ return 1;
406
+ }
407
+ fwrite(text, copy_str->size, 1, f);
408
+ fclose(f);
409
+ clear_bridge(copy);
410
+ n_examples_written++;
411
+ }
412
+ }
413
+ continue;
414
+ #endif
415
+
416
+ // Check for idempotency.
417
+ n = compare(bridge_str, bridge_str);
418
+ if (n == -1) {
419
+ printf("idempotency test failed\n");
420
+ exit(1);
421
+ }
422
+ printf("compare is idempotent\n");
423
+
424
+ // Check for inverse.
425
+ text = unparse_bridge(bridge);
426
+ if (strncmp(text, raw, strlen(raw) - 10) != 0) {
427
+ printf("inverse test failed:\n%s\n", text);
428
+ exit(1);
429
+ }
430
+ Safefree(text);
431
+ printf("parse and unparse are inverses\n");
432
+
433
+ // Hash the bridge.
434
+ hash_bridge(bridge, raw_hash);
435
+ printf("hash=%s\n", hex_str(raw_hash, HASH_SIZE, buf));
436
+
437
+ // Check that a bunch of randomized versions of the same bridge
438
+ // compare equal to the original.
439
+ printf("permuting...");
440
+ #define N_PERMS 200
441
+ for (i = 0; i < N_PERMS; i++) {
442
+
443
+ vary(copy, bridge, i*13+1);
444
+
445
+ copy_str->ptr = unparse_bridge(copy);
446
+ copy_str->size = strlen(copy_str->ptr);
447
+
448
+ // printf("Permutation %d:\n%s\n", i+1, text);
449
+ n = compare(copy_str, bridge_str);
450
+ if (n == -1) {
451
+ printf("comparison with permutation failed at i=%d.\n", i);
452
+ exit(1);
453
+ }
454
+ else if ( !n ) {
455
+ printf("are_same test failed at i=%d\n", i);
456
+ exit(1);
457
+ }
458
+ Safefree(copy_str->ptr);
459
+
460
+ // Hash the variant.
461
+ hash_bridge(copy, variant_hash);
462
+ if (memcmp(raw_hash, variant_hash, HASH_SIZE) != 0) {
463
+ printf("hash test failed at i=%d.\n", i);
464
+ exit(1);
465
+ }
466
+
467
+ if (i % 1000 == 999)
468
+ printf(" %d", i+1);
469
+ }
470
+ printf("\n");
471
+
472
+ // Check that randomly perturbed versions of the bridge compare
473
+ // unequal.
474
+
475
+ printf("perturbing...");
476
+ #undef N_PERMS
477
+ #define N_PERMS 200
478
+ for (i = 0; i < N_PERMS; i++) {
479
+ perturb(copy, bridge, i*13+1, 1, 0);
480
+
481
+ copy_str->ptr = unparse_bridge(copy);
482
+ copy_str->size = strlen(copy_str->ptr);
483
+
484
+ // printf("Permutation %d:\n%s\n", i+1, text);
485
+ n = compare(copy_str, bridge_str);
486
+ if (n == -1) {
487
+ printf("comparison with perturbation failed at i=%d.\n", i);
488
+ exit(1);
489
+ }
490
+ else if ( n ) {
491
+ printf("are_same test failed to detect difference at i=%d\n", i);
492
+ exit(1);
493
+ }
494
+ Safefree(copy_str->ptr);
495
+
496
+ // Hash the variant.
497
+ hash_bridge(copy, variant_hash);
498
+ if (memcmp(raw_hash, variant_hash, HASH_SIZE) == 0) {
499
+ printf("warning: hash miss at i=%d.\n", i);
500
+ }
501
+
502
+ if (i % 1000 == 999)
503
+ printf(" %d", i+1);
504
+ }
505
+ printf("\n");
506
+
507
+ // Do an analysis table.
508
+ text = analysis_table(bridge_str);
509
+
510
+ if (text) {
511
+ printf("writing analysis...");
512
+ _splitpath(file_data->name, 0, path, name, ext);
513
+ sprintf(buf, "Debug/html/%s.htm", name);
514
+ f = fopen(buf, "w");
515
+ if (!f) {
516
+ fprintf(stderr, "can't open %s for output", buf);
517
+ return 4;
518
+ }
519
+ fputs("<html><head><title>Load Test Results Report</title>\n"
520
+ "<style>\n"
521
+ " table { font-size : 8pt; font-family: arial, helvetica, sans-serif }\n"
522
+ " th { font-weight : bold }\n"
523
+ "</style></head><body>\n", f);
524
+ fputs(text, f);
525
+ fputs("</body></html>", f);
526
+ fclose(f);
527
+
528
+ Safefree(text);
529
+ }
530
+
531
+ //print_analysis(stdout, anal, bridge, geometry, loading, params);
532
+ //printf("Cost = $%.2lf\n", (double)bridge_cost(bridge, geometry, params));
533
+
534
+ clear_bridge(bridge);
535
+ clear_bridge(copy);
536
+ clear_geometry(geometry);
537
+ clear_params(params);
538
+ clear_loading(loading);
539
+ clear_analysis(anal);
540
+
541
+ // Do a sketch
542
+ sketch(bridge_str, 300, 300, compressed_image, result);
543
+ _splitpath(file_data->name, 0, path, name, ext);
544
+ sprintf(buf, "Debug/sketch/%s.png", name);
545
+ f = fopen(buf, "wb");
546
+ if (!f) {
547
+ fprintf(stderr, "can't open %s for output", buf);
548
+ return 3;
549
+ }
550
+ fwrite(compressed_image->data, compressed_image->filled, 1, f);
551
+ fclose(f);
552
+ }
553
+ clear_compressed_image(compressed_image);
554
+
555
+ #ifdef WRITE_EXAMPLES
556
+ printf("%d examples read, %d written\n", n_examples_read, n_examples_written);
557
+ #endif
558
+
559
+
560
+ return 0;
561
+ }
562
+
563
+ #endif
data/ext/WPBDC/judge.h ADDED
@@ -0,0 +1,142 @@
1
+ #ifndef _JUDGE_H
2
+ #define _JUDGE_H
3
+
4
+ /*
5
+ *
6
+ * judge.h -- Ruby Extension WPBDC. Support for the West Point
7
+ * Bicentennial Design Contest web site. Ruby-visible constants
8
+ * and functions.
9
+ */
10
+
11
+ #include "rubydefs.h"
12
+ #include "sketch.h"
13
+
14
+ #define CONTEST_YEAR 2012
15
+ #define YEAR_TO_VERSION(Y) (((((Y) % 100) / 10) << 8) | (((Y) % 10) << 4) | 0x400c)
16
+ #define BD_VERSION YEAR_TO_VERSION(CONTEST_YEAR)
17
+
18
+ // Status returns from analyze().
19
+ #define BRIDGE_OK 0
20
+ #define BRIDGE_MALFORMED 1
21
+ #define BRIDGE_WRONGVERSION 2
22
+ #define BRIDGE_FAILEDTEST 3
23
+
24
+ // Maximum element counts. Increased for 2012.
25
+ // #define MAX_JOINTS 50
26
+ // #define MAX_MEMBERS 120
27
+ #define MAX_JOINTS 100
28
+ #define MAX_MEMBERS 200
29
+
30
+ // Size of hash value in bytes.
31
+ #define HASH_SIZE 20
32
+
33
+ // Possible errors during bridge parsing.
34
+ typedef enum bridge_err_t {
35
+ BridgeNoError,
36
+ BridgeMissingData,
37
+ BridgeSyntax,
38
+ BridgeMissingHeader,
39
+ BridgeBadHeader,
40
+ BridgeMissingTestStatus,
41
+ BridgeBadTestStatus,
42
+ BridgeBadScenario,
43
+ BridgeBadNDesignIterations,
44
+ BridgeTooManyElements,
45
+ BridgeBadJointBanner,
46
+ BridgeTooFewJoints,
47
+ BridgeWrongPrescribedJoints,
48
+ BridgeBadMemberBanner,
49
+ BridgeTooFewMembers,
50
+ BridgeBadLabelPos,
51
+ BridgeExtraJunk,
52
+ BridgeDupJoints,
53
+ BridgeDupMembers,
54
+ BridgeJointOnMember,
55
+ BridgeBadLoadScenario,
56
+ BridgeBadChar,
57
+ } TBridgeError;
58
+
59
+ // Test status flag values read from bridge file.
60
+ typedef enum test_status_t {
61
+ NullTestStatus = -1,
62
+ Unrecorded,
63
+ Untested,
64
+ Failed,
65
+ Passed,
66
+ } TTestStatus;
67
+
68
+ #define SCENARIO_ID_SIZE 10
69
+ #define SCENARIO_NUMBER_SIZE 3
70
+
71
+ // WPBD version number.
72
+ typedef unsigned short TVersion; // 2 bytes
73
+
74
+ // Struct that describes what happened in analysis.
75
+ // We don't typedef it as a cue that it isn't covered
76
+ // by the standard init-setup-clear management protocol.
77
+ struct analysis_result_t {
78
+ TVersion version; // WPBDC version of parsed bridge.
79
+ char scenario_id[SCENARIO_ID_SIZE]; // WPBDC scenario ID.
80
+ char scenario_number[SCENARIO_NUMBER_SIZE]; // WPBDC scenario number.
81
+ TTestStatus test_status; // Load test flag from uploaded file.
82
+ int status; // Summary status
83
+ TBridgeError error; // Bridge error condition.
84
+ double cost; // Cost in dollars.
85
+ char hash[HASH_SIZE]; // Bridge hash value.
86
+ };
87
+
88
+ // This must match ruby string length type.
89
+ typedef long STRLEN;
90
+
91
+ // Representation for a string of arbitrary length, which may possibly
92
+ // contain NULLs. We'll convert RSTRINGs to and from this type.
93
+ typedef struct t_string {
94
+ char *ptr;
95
+ STRLEN size;
96
+ } STRING;
97
+
98
+ // Encrypt/decrypt a bridge by modifying string in place.
99
+ void endecrypt(STRING *bridge_as_string);
100
+
101
+ // Parse bridge as string.
102
+ // Return a result struct that describes what happened.
103
+ void analyze(STRING *bridge_as_string, struct analysis_result_t *result);
104
+
105
+ // Compare two bridges for functional equality, that is geometry
106
+ // identical up to joint and member numbers.
107
+ // Returns -1 if one or both bridges were malformed.
108
+ // Else returns a boolean indicating equality.
109
+ #define BRIDGE_COMPARISON_ERROR -1
110
+ #define BRIDGES_EQUAL 1
111
+ #define BRIDGES_NOT_EQUAL 0
112
+ int compare(STRING *bridge_as_string_a, STRING *bridge_as_string_b);
113
+
114
+ // Return a variant of a bridge with permuted joints, members, and joint references
115
+ // with members. This is for testing comparison and hashing functions.
116
+ char *variant(STRING *bridge_as_string, int seed);
117
+
118
+ // Return a variant of a bridge where one member chosen at random has been reduced
119
+ // in size until the bridge fails. This is for testing member force computations.
120
+ char *failed_variant(STRING *bridge_as_string, int seed);
121
+
122
+ // Return a perturbation of a bridge with the given number of joints moved by
123
+ // one grid and/or the given number of members thickened (or sometimes thinned).
124
+ // The resulting bridge has _no_ guarantee of correctness, let alone that it will
125
+ // carry any kind of load.
126
+ char *perturbation(STRING *bridge_as_string, int seed,
127
+ int n_joints, int n_members);
128
+
129
+ // Do an analysis and also return a PNG image sketch of the bridge.
130
+ void sketch(STRING *bridge_as_string,
131
+ int width, int height,
132
+ COMPRESSED_IMAGE *compressed_image,
133
+ struct analysis_result_t *result);
134
+
135
+ // Return HTML for a table of analysis data.
136
+ char *analysis_table(STRING *bridge_as_string);
137
+
138
+ // Accept a string containing a scenario number.
139
+ // Return a pointer to the 3-character local contest code or null if no match.
140
+ char *get_local_contest_number(STRING *number_as_string);
141
+
142
+ #endif