home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume40 / queens / part01 < prev    next >
Encoding:
Text File  |  1993-10-10  |  19.0 KB  |  550 lines

  1. Newsgroups: comp.sources.misc
  2. From: bert@netcom.com (Roberto Sierra)
  3. Subject: v40i003:  queens - N-Queens Chess Solver, Part01/01
  4. Message-ID: <1993Oct11.013103.16689@sparky.sterling.com>
  5. X-Md4-Signature: 5aa8b3ee711a9180710d0a58758d5fbb
  6. Keywords: ANSI C program to solve N Queens Chess Problem
  7. Sender: kent@sparky.sterling.com (Kent Landfield)
  8. Organization: Tempered MicroDesigns
  9. Date: Mon, 11 Oct 1993 01:31:03 GMT
  10. Approved: kent@sparky.sterling.com
  11.  
  12. Submitted-by: bert@netcom.com (Roberto Sierra)
  13. Posting-number: Volume 40, Issue 3
  14. Archive-name: queens/part01
  15. Environment: Sun, Mac, ANSI-C
  16.  
  17. I'm sure that there are a bazillion solutions to the Eight Queens
  18. chess problem floating around on the net, but I'm still quite
  19. proud of the solution I came up with way back in '84.  I can't
  20. resist showing it off to everyone.  Amazingly, it still works!! ;-)
  21.  
  22. The Eight Queens problem, for those who don't know, involves
  23. finding all the ways that you can place 8 queens on a chess board
  24. so that no queen can capture any other -- that is, no more than
  25. a single queen appears on any rank, file or diagonal.  Try to
  26. do it yourself -- it's *really* hard.
  27.  
  28. On a Sun or Mac, my program will find all 92 8x8 solutions
  29. in a fraction of a second.  OK -- so there are actually only
  30. 23 unique solutions -- my program doesn't exploit rotational
  31. symmetries to report only unique solutions.  If you have an
  32. ANSI C compiler, you should be able to get it up and running
  33. in minutes.  Instructions are included in the source code.
  34.  
  35. The heart of the code is a recursive pruning algorithm that
  36. zeros in on solutions quickly without wasting a lot of time.
  37. I've found that it's an excellent way to teach recursive
  38. programming techniques to people.  Also, great care has
  39. been taking to minimize the amount of time it takes to
  40. detect when queens lie on the same rank, file or diagonal.
  41. I'm particularly proud of how I did that.
  42.  
  43. I recently dug up the code to solve another unrelated
  44. problem, ANSI-fied it in the process, and decided that
  45. it was about time I posted it for all to see.
  46.  
  47. Have fun with it!!
  48.  
  49. some examples...
  50.  
  51. $ Queens 8   ## Nearly instantaneous
  52. 8 queens on a 8x8 board...
  53.  Q - - - - - - -
  54.  - - - - Q - - -
  55.  - - - - - - - Q
  56.  - - - - - Q - -
  57.  - - Q - - - - -
  58.  - - - - - - Q -
  59.  - Q - - - - - -
  60.  - - - Q - - - -
  61.  
  62. $ Queens -c 8  ## Count all 8x8 solutions.  <1 second.
  63. 8 queens on a 8x8 board...
  64. ...there are 92 solutions
  65.  
  66. $ Queens -c 12  ## Count all 12x12 solutions. About 20 seconds.
  67. 12 queens on a 12x12 board...
  68. ...there are 14200 solutions
  69.  
  70.  
  71.    Roberto Sierra
  72.    Tempered MicroDesigns
  73.    San Francisco, CA
  74. ---
  75. #! /bin/sh
  76. # This is a shell archive.  Remove anything before this line, then feed it
  77. # into a shell via "sh file" or similar.  To overwrite existing files,
  78. # type "sh file -c".
  79. # Contents:  Queens.c README
  80. # Wrapped by kent@sparky on Sun Oct 10 20:29:03 1993
  81. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  82. echo If this archive is complete, you will see the following message:
  83. echo '          "shar: End of archive."'
  84. if test -f 'Queens.c' -a "${1}" != "-c" ; then 
  85.   echo shar: Will not clobber existing file \"'Queens.c'\"
  86. else
  87.   echo shar: Extracting \"'Queens.c'\" \(13250 characters\)
  88.   sed "s/^X//" >'Queens.c' <<'END_OF_FILE'
  89. X/*
  90. X**  Queens.c    --  Find solutions to the Eight-Queens chess problem.
  91. X**            Roberto Sierra  3/19/84  Version 1.1
  92. X**
  93. X**  Description:
  94. X**    This program finds all the possible ways that N queens can
  95. X**    be placed on an NxN chessboard so that the queens cannot
  96. X**    capture one another -- that is, so that no rank, file or
  97. X**    diagonal is occupied by more than one queen.  By default,
  98. X**    the program prints the first solution it finds.  You can
  99. X**    use the -a option to print all solutions, or the -c option
  100. X**    just to count them.  The program allows the chess board
  101. X**    to be from 1x1 (trivial case) to 100x100.  Warning: the
  102. X**    larger the chess board, the longer it typically takes to
  103. X**    find each solution, even though there may be more of them.
  104. X**
  105. X**    This is a terrific example of the utility of recursion.  The
  106. X**    algorithm uses recursion to drastically limit the number
  107. X**    of board positions that are tested.  The program is able
  108. X**    to find all 8x8 queen solutions in a fraction of a second
  109. X**    (not counting print time).  The code makes no attempt to
  110. X**    eliminate symmetrical solutions, so the number of solutions
  111. X**    reported will always be higher than the actual number of
  112. X**    distinct solutions.
  113. X**
  114. X**
  115. X**  Usage:
  116. X**    Queens [-ac] n
  117. X**
  118. X**    n    number of queens (rows and columns).
  119. X**        An integer from 1 to 100.
  120. X**    -a    Find (and print) all solutions.
  121. X**    -c    Count all solutions, but do not print them.
  122. X**
  123. X**    The output is sent to stdout.  All errors messages are
  124. X**    sent to stderr.  If a problem arises, the return code is -1.
  125. X**
  126. X**
  127. X**  Examples:
  128. X**
  129. X**    Queens 8    ## Show an 8x8 solution
  130. X**    8 queens on a 8x8 board...
  131. X**     Q - - - - - - -
  132. X**     - - - - Q - - -
  133. X**     - - - - - - - Q
  134. X**     - - - - - Q - -
  135. X**     - - Q - - - - -
  136. X**     - - - - - - Q -
  137. X**     - Q - - - - - -
  138. X**     - - - Q - - - -
  139. X**
  140. X**    Queens -c 8    ## Count all 8x8 solutions
  141. X**    8 queens on a 8x8 board...
  142. X**    ...there are 92 solutions.
  143. X**
  144. X**    Queens -a 4    ## Show all 4x4 solutions
  145. X**    4 queens on a 4x4 board...
  146. X**    
  147. X**    Solution #1:
  148. X**     - Q - -
  149. X**     - - - Q
  150. X**     Q - - -
  151. X**     - - Q -
  152. X**    
  153. X**    Solution #2:
  154. X**     - - Q -
  155. X**     Q - - -
  156. X**     - - - Q
  157. X**     - Q - -
  158. X**    
  159. X**    ...there are 2 solutions.
  160. X**
  161. X**
  162. X**  Build Instructions:
  163. X**    You'll need an ANSI C compiler (or the willingness to edit
  164. X**    the program a bit).  If you've got Gnu C, then you can
  165. X**    compile and load the program as follows:
  166. X**
  167. X**        gcc Queens.c -ansi -o Queens
  168. X**
  169. X**    [If you're using MPW on the Mac, define '-d MPW' on the
  170. X**    compile line so that background processing will occur.]
  171. X**
  172. X**
  173. X**  Algorithm:
  174. X**    In a 1984 Byte article, I ran across an interesting letter
  175. X**    from a high school student who was attempting to solve the
  176. X**    Eight Queens problem using a BASIC interpreter.  He had
  177. X**    developed a program which placed eight queens successively
  178. X**    on all sixty-four squares, testing for conflicts at each
  179. X**    iteration.  Of course, such a program would require 64^8
  180. X**    iterations (about 2.8x10^14 iterations).  Even in C on a,
  181. X**    fast CPU, this could take months or years.  Byte's answer was
  182. X**    to alter the loops so that the queens resided on separate
  183. X**    ranks, thereby reducing the number of iterations required
  184. X**    to find all solutions to 8^8 iterations (about 16 million).
  185. X**    More reasonable, but still requiring a chunk of CPU time.
  186. X**
  187. X**    I puzzled about this problem a bit, and came to realize that
  188. X**    this was still wasting a lot of CPU cycles.  Though I'm sure
  189. X**    others have come up with good algorithms, I decided to come
  190. X**    up with my own, with a particular eye on efficiency.  The
  191. X**    resulting algorithm finds all 8x8 solutions in a fraction
  192. X**    of a second (there are 92 solutions, including rotations).
  193. X**    On a Sun 4, it'll find all 365,596 solutions on a 14x14 board
  194. X**    in a bit over 2 minutes (printing them out requires extra
  195. X**    time, of course).  Even Byte's solution would require 14^14
  196. X**    iterations (about 10^16) which would take aeons.
  197. X**
  198. X**    My algorithm works as follows:
  199. X**    (1)  Place a queen in the top left corner.
  200. X**    (2)  Place another queen immediately below.
  201. X**    (3)  Test for conflicts.  If the second queen conflicts (it
  202. X**         does at first), then move it one square to the right.
  203. X**    (4)  Loop step 3 until there are no conflicts.  Place
  204. X**         the next queen on the board and recurse.
  205. X**    (5)  If any queen reaches the right edge of the board,
  206. X**         remove it and 'pop' to the previous recursion level.
  207. X**    (6)  Now repeat these steps recursively until all eight
  208. X**         queens (or however many) have been placed without
  209. X**         conflict -- the result is a solution to the problem,
  210. X**         which is counted and optionally printed.
  211. X**
  212. X**    Because conflicts are tested as the recursion proceeds,
  213. X**    this has the effect of 'pruning' the recursion so that
  214. X**    a large number of board positions are not even attempted.
  215. X**    The result is that the algorithm runs in reasonable time.
  216. X**
  217. X**    I used a few tricks to make the test-for-conflict code
  218. X**    extremely efficient -- there is no 'inner' loop to search
  219. X**    along ranks, files, or diagonals.  A series of arrays are
  220. X**    maintained instead which indicate which queen currently
  221. X**    'owns' each rank, file or diagonal.  This makes the
  222. X**    algorithm really fly, though the code is a little hard to
  223. X**    read.  Lastly, pointer arithmetic is used to reduce the
  224. X**    number of implicit multiplications used in array addressing.
  225. X**
  226. X**
  227. X**  Contact:
  228. X**    For queries regarding this program, contact Roberto Sierra
  229. X**    at any of the following addresses:
  230. X**
  231. X**        Roberto Sierra
  232. X**        bert@netcom.com   (preferred address)
  233. X**        73557.2101@compuserve.com
  234. X**
  235. X**        Tempered MicroDesigns
  236. X**        P.O. Box 170638
  237. X**        San Francisco, CA  94117
  238. X**
  239. X**
  240. X**  Fine Print:
  241. X**    This program is in the public domain and can be used for
  242. X**    any purpose whatsoever, including commercial application.
  243. X**    [I'd like to hear what you do with it, though.]
  244. X**    Absolutely no warranty or liability is implied or extended
  245. X**    by the author.
  246. X**
  247. X**
  248. X**  Modification History:
  249. X**    PRS  3/19/84  v1.0 -- Original version.
  250. X**    PRS  7/25/93  v1.1 -- ANSIfied the code.  More efficient pointers.
  251. X**
  252. X*/
  253. X
  254. X
  255. X#include <stdio.h>                /* Need standard I/O functions */
  256. X#include <stdlib.h>                /* Need exit() routine interface */
  257. X#include <string.h>                /* Need strcmp() interface */
  258. X#ifdef    MPW                    /* Macintosh MPW ONLY */
  259. X#include <CursorCtl.h>                /* Need cursor control interfaces */
  260. X#endif
  261. X
  262. X#define MAXQUEENS   100             /* Maximum number of queens */
  263. X#define MAXRANKS    MAXQUEENS            /* Maximum number of ranks (rows) */
  264. X#define MAXFILES    MAXQUEENS            /* Maximum number of files (columns) */
  265. X#define MAXDIAGS    (MAXRANKS+MAXFILES-1)    /* Maximum number of diagonals */
  266. X#define EMPTY        (MAXQUEENS+1)        /* Marks unoccupied file or diagonal */
  267. X
  268. X/* GLOBAL VARIABLES */
  269. X
  270. Xint        queens;                /* Number of queens to place */
  271. Xint        ranks;                /* Number of ranks (rows) */
  272. Xint        files;                /* Number of files (columns) */
  273. Xint        printing = 1;            /* TRUE if printing positions */
  274. Xint        findall = 0;            /* TRUE if finding all solutions */
  275. X
  276. Xunsigned long    solutions = 0;            /* Number of solutions found */
  277. Xint        queen[MAXRANKS];        /* File on which each queen is located */
  278. Xint        file[MAXFILES];         /* Which queen 'owns' each file */
  279. Xint        fordiag[MAXDIAGS];        /* Which queen 'owns' forward diagonals */
  280. Xint        bakdiag[MAXDIAGS];        /* Which queen 'owns' reverse diagonals */
  281. Xchar        *progname = 0;            /* The name of this program */
  282. X
  283. X
  284. X
  285. X
  286. X
  287. X/***********************/
  288. X/****    ROUTINES    ****/
  289. X/***********************/
  290. X
  291. X/* Internal prototypes */
  292. Xvoid    main(int argc,char **argv);        /* Main program */
  293. Xvoid    find(int level);            /* Algorithm to find solutions */
  294. Xvoid    pboard(void);                /* Print a solution */
  295. X
  296. X
  297. X
  298. X
  299. X/*---------------------- main() ---------------------------
  300. X**  MAIN program.  The main purpose of this routine is
  301. X**  to deal with decoding the command line arguments,
  302. X**  initializing the various arrays, and starting the
  303. X**  recursive search routine.
  304. X*/
  305. X
  306. Xvoid main(int argc,char **argv)
  307. X{
  308. X    register int    i;                /* Loop variable */
  309. X    register char   *p;             /* Pointer to argument */
  310. X
  311. X#ifdef    MPW                    /* Macintosh MPW ONLY */
  312. X    InitCursorCtl(0);                /* Enable cursor control */
  313. X#endif
  314. X
  315. X    progname = argv[0];             /* The name of the program */
  316. X
  317. X    /****   DECODE COMMAND LINE ARGUMENTS   ****/
  318. X
  319. X    for (i=1; i<argc; ++i) {            /* Scan through arguments */
  320. X    p = argv[i];                /* Pointer to base of argument */
  321. X    if (*p == '-') {            /* Command line option? */
  322. X        while (*++p) {            /* Loop through characters */
  323. X        switch (*p) {            /* What is the character */
  324. X        case 'a':            /* '-a' option */
  325. X            findall = 1;        /* Set flag to find all solutions */
  326. X            break;
  327. X        case 'c':            /* '-c' option */
  328. X            printing = 0;        /* Counting, not printing */
  329. X            findall = 1;        /* Also forces findall option */
  330. X            break;
  331. X        default:            /* Illegal option */
  332. X            fprintf(stderr,"%s: Illegal option '%s'\n",progname,argv[i]);
  333. X            fprintf(stderr,"usage: %s [-ac] queens\n",progname);
  334. X            exit(-1);
  335. X        }                /* End of switch */
  336. X        }                    /* End of loop */
  337. X    } else {                /* End of option test */
  338. X        if (sscanf(p,"%d",&queens) != 1) {    /* Read integer argument */
  339. X        fprintf(stderr,"%s: non-integer argument '%s'\n",progname,p);
  340. X        exit(-1);
  341. X        }
  342. X        if (queens <= 0) {            /* N must be positive */
  343. X        fprintf(stderr,"%s: queens must be positive integer\n",progname);
  344. X        exit(-1);
  345. X        }
  346. X        if (queens > MAXQUEENS) {        /* N can't be too large */
  347. X        fprintf(stderr,"%s: can't have more than %d queens\n",
  348. X            progname, MAXQUEENS);
  349. X        exit(-1);
  350. X        }
  351. X    }                    /* End of argument test */
  352. X    }                        /* End of argument scan loop */
  353. X    if (queens == 0) {
  354. X    fprintf(stderr,"%s: missing queens argument\n",progname);
  355. X    fprintf(stderr,"usage: %s [-ac] queens\n",progname);
  356. X    exit(-1);
  357. X    }
  358. X
  359. X
  360. X    ranks = files = queens;            /* NxN board for N queens */
  361. X    printf("%d queen%s on a %dx%d board...\n",
  362. X        queens, queens>1? "s" : "", ranks, files);
  363. X    fflush(stdout);
  364. X
  365. X    /*    Initialization    */
  366. X    solutions = 0;                /* No solutions yet */
  367. X    for (i=0; i<MAXFILES; ++i) file[i] = EMPTY;
  368. X    for (i=0; i<MAXDIAGS; ++i) fordiag[i] = bakdiag[i] = EMPTY;
  369. X
  370. X    /* Find all solutions (begin recursion) */
  371. X    find(0);
  372. X    if (printing && solutions) putchar('\n');
  373. X
  374. X    /* Report results */
  375. X    if (solutions == 1) {
  376. X    printf("...there is 1 solution\n");
  377. X    } else {
  378. X    printf("...there are %ld solutions\n", solutions);
  379. X    }
  380. X
  381. X    exit(0);                    /* No errors */
  382. X}                        /* End of main() */
  383. X
  384. X
  385. X
  386. X/*-------------------------- find() ----------------------------
  387. X**  FIND is the recursive heart of the program, and finds all
  388. X**  solutions given a set of level-1 fixed queen positions.
  389. X**  The routine moves a single queen through all files (columns)
  390. X**  at the current rank (recursion level).  As the queen is moved,
  391. X**  conflict tests are made.  If the queen can be placed without
  392. X**  conflict, then the routine recurses to the next level.  When
  393. X**  all queens have been placed without conflict, a solution is
  394. X**  counted and reported.
  395. X*/
  396. X
  397. Xvoid find(register int level)
  398. X{
  399. X    register int    f;                /* Indexes through files */
  400. X    register int    *fp,*fdp,*bdp;        /* Ptrs to file/diagonal entries */
  401. X
  402. X#ifdef    MPW                    /* Macintosh MPW ONLY */
  403. X    if (level & 7 == 0) {            /* Periodically break for... */
  404. X    SpinCursor(1);                /* background processing */
  405. X    }
  406. X#endif
  407. X
  408. X    if (level == queens) {            /* Placed all queens?  Stop. */
  409. X    ++solutions;                /* Congrats, this is a solution! */
  410. X    if (printing) pboard();         /* Print board if printing */
  411. X    if (!findall) exit(0);            /* May stop after first solution */
  412. X#ifdef    MPW                    /* Macintosh MPW ONLY */
  413. X    SpinCursor(1);                /* Allow background processing */
  414. X#endif
  415. X    } else {                    /* Not at final level yet */
  416. X    
  417. X    for (                    /* MOVE QUEEN THROUGH ALL FILES */
  418. X        f = 0,                /* Queen starts at left (file 0) */
  419. X        fp = file,                /* Ptr to base of file array */
  420. X        fdp = &fordiag[level],        /* Ptr to first fwd diag entry */
  421. X        bdp = &bakdiag[level+files-1]    /* Ptr to first bak diag entry */
  422. X    ;
  423. X        f < files                /* Loop through all files */
  424. X    ;
  425. X        ++f,                /* Advance index */
  426. X        ++fp, ++fdp, --bdp            /* Advance pointers */
  427. X    ) {
  428. X        if (*fp >= level &&         /* No queen on the file? */
  429. X        *fdp >= level && *bdp >= level    /* No queens on diagonals? */
  430. X        ) {
  431. X        queen[level] = f;        /* Note new position of queen */
  432. X        *fp = *fdp = *bdp = level;    /* Place queen on file & diags */
  433. X        find(level+1);            /* This level OK, recurse to next */
  434. X        *fp = *fdp = *bdp = EMPTY;    /* Remove queen from file & diags */
  435. X        }                    /* End of conflict test */
  436. X    }                    /* End of file loop */
  437. X    }                        /* End if (level == queens) */
  438. X}                        /* End of find() */
  439. X
  440. X
  441. X
  442. X
  443. X/*------------------------- pboard() -----------------------
  444. X**  This routines prints the board for a particular solution.
  445. X**  The output is sent to stdout.
  446. X*/
  447. X
  448. Xvoid pboard(void)
  449. X{
  450. X    register int    i,j;            /* Rank/File indices */
  451. X
  452. X    if (findall) {                /* Only if searching for all */
  453. X    printf("\nSolution #%lu:\n",solutions);    /* Print solution number */
  454. X    }
  455. X    for (i=0; i<ranks; ++i) {            /* Loop through all ranks */
  456. X    for (j=0; j<files; ++j) {        /* Loop through all files */
  457. X        putchar(' ');            /* Output a space */
  458. X        if (j==queen[i]) putchar('Q');    /* Output Q for queen... */
  459. X        else putchar('-');            /* or '-' if empty */
  460. X    }
  461. X    putchar('\n');                /* Break line */
  462. X    }
  463. X    fflush(stdout);                /* Flush solution to output */
  464. X}                        /* End of pboard() */
  465. X
  466. X
  467. X
  468. X
  469. X
  470. X/****    End of Queens.c ****/
  471. X
  472. END_OF_FILE
  473.   if test 13250 -ne `wc -c <'Queens.c'`; then
  474.     echo shar: \"'Queens.c'\" unpacked with wrong size!
  475.   fi
  476.   # end of 'Queens.c'
  477. fi
  478. if test -f 'README' -a "${1}" != "-c" ; then 
  479.   echo shar: Will not clobber existing file \"'README'\"
  480. else
  481.   echo shar: Extracting \"'README'\" \(1999 characters\)
  482.   sed "s/^X//" >'README' <<'END_OF_FILE'
  483. X
  484. XI'm sure that there are a bazillion solutions to the Eight Queens
  485. Xchess problem floating around on the net, but I'm still quite
  486. Xproud of the solution I came up with way back in '84.  I can't
  487. Xresist showing it off to everyone.  Amazingly, it still works!! ;-)
  488. X
  489. XThe Eight Queens problem, for those who don't know, involves
  490. Xfinding all the ways that you can place 8 queens on a chess board
  491. Xso that no queen can capture any other -- that is, no more than
  492. Xa single queen appears on any rank, file or diagonal.  Try to
  493. Xdo it yourself -- it's *really* hard.
  494. X
  495. XOn a Sun or Mac, my program will find all 92 8x8 solutions
  496. Xin a fraction of a second.  OK -- so there are actually only
  497. X23 unique solutions -- my program doesn't exploit rotational
  498. Xsymmetries to report only unique solutions.  If you have an
  499. XANSI C compiler, you should be able to get it up and running
  500. Xin minutes.  Instructions are included in the source code.
  501. X
  502. XThe heart of the code is a recursive pruning algorithm that
  503. Xzeros in on solutions quickly without wasting a lot of time.
  504. XI've found that it's an excellent way to teach recursive
  505. Xprogramming techniques to people.  Also, great care has
  506. Xbeen taking to minimize the amount of time it takes to
  507. Xdetect when queens lie on the same rank, file or diagonal.
  508. XI'm particularly proud of how I did that.
  509. X
  510. XI recently dug up the code to solve another unrelated
  511. Xproblem, ANSI-fied it in the process, and decided that
  512. Xit was about time I posted it for all to see.
  513. X
  514. XHave fun with it!!
  515. X
  516. Xsome examples...
  517. X
  518. X$ Queens 8   ## Nearly instantaneous
  519. X8 queens on a 8x8 board...
  520. X Q - - - - - - -
  521. X - - - - Q - - -
  522. X - - - - - - - Q
  523. X - - - - - Q - -
  524. X - - Q - - - - -
  525. X - - - - - - Q -
  526. X - Q - - - - - -
  527. X - - - Q - - - -
  528. X
  529. X$ Queens -c 8  ## Count all 8x8 solutions.  <1 second.
  530. X8 queens on a 8x8 board...
  531. X...there are 92 solutions
  532. X
  533. X$ Queens -c 12  ## Count all 12x12 solutions. About 20 seconds.
  534. X12 queens on a 12x12 board...
  535. X...there are 14200 solutions
  536. X
  537. X
  538. X   Roberto Sierra
  539. X   Tempered MicroDesigns
  540. X   San Francisco, CA
  541. END_OF_FILE
  542.   if test 1999 -ne `wc -c <'README'`; then
  543.     echo shar: \"'README'\" unpacked with wrong size!
  544.   fi
  545.   # end of 'README'
  546. fi
  547. echo shar: End of archive.
  548. exit 0
  549. exit 0 # Just in case...
  550.