/*
* scramble.c, by Kimmo Kulovesi , 2004-05-02
*
* This reads text from stdin and scrambles each word (composed of
* alphabetic characters as detected by "isalpha()") so that the first
* and last letter stay in place and all letters in between are
* moved around randomly.
*
*
* The point was to test the idea behind the following piece of text that
* has been circulating around the Internet in various incarnations:
*
* Aoccdrnig to a rscheearch at Cmabrigde Uinervtisy, it deosn't mttaer
* in waht oredr the ltteers in a wrod are, the olny iprmoetnt tihng is
* taht the frist and lsat ltteer be at the rghit pclae. The rset can be
* a toatl mses and you can sitll raed it wouthit porbelm. Tihs is
* bcuseae the huamn mnid deos not raed ervey lteter by istlef, but the
* wrod as a wlohe.
*
* That is:
*
* According to a researcher at Cambridge University, it doesn't matter
* in what order the letters in a word are, the only important thing is
* that the first and last letter be at the right place. The rest can be
* a total mess and you can still read it without problem. This is
* because the human mind does not read every letter by itself, but the
* word as a whole.
*
* (The part about Cambridge University etc is almost certainly false.)
*
* NOTE! This program is the result of one Sunday afternoon of boredom
* and is not very well planned or checked for errors. Use freely at your
* own risk only, and distribute as you will.
*/
#include
#include
#include
#include
#include
#define MAX_LENGTH_OF_WORD 15 /* Maximum length of word to buffer */
#define MAX_DISTANCE 3 /* Maximum distance of letters to swap */
#define MAX_RETRIES 8 /* Maximum retries for failed swaps */
/*
* MAX_LENGTH_OF_WORD doesn't need to be excessively large - words
* longer than that are simply scrambled in smaller parts, which
* just prevents scrambling from occurring over the boundary of
* such parts, and causes each part to have a non-scrambling first
* and last letter.
*
* MAX_DISTANCE tries to make the scrambled text easier to read by
* not swapping letters too far away from their original position in case
* of really long words. However, MAX_DISTANCE is not strictly enforced -
* repeated swapping can make letters end up further and further away.
*
* MAX_RETRIES is the maximum number of times to retry swapping letters
* when scrambling has failed. Setting this larger increases the
* likelihood of scrambling the word from its original order, but also
* makes the program run slower.
*/
static int swap(char *const, const unsigned int, const unsigned int);
static void scramble(char *const, const unsigned int);
int
main(void)
{
char word[MAX_LENGTH_OF_WORD + 1];
char *p; /* Pointer to position in buffer */
int bufleft; /* Room left in the buffer */
int c; /* Last character read */
(void) srand(time(NULL));
p = word;
bufleft = sizeof(word) - 1;
while ((c = getchar()) != EOF) {
/*
* Consider only "words" formed by alphabetic characters:
*/
if (bufleft > 0 && isalpha(c)) {
*p++ = c;
bufleft--;
continue; /* !!!! */
}
/*
* If there is a word in the buffer, scramble and print it:
*/
if (p != word) {
*p = '\0';
scramble(word, p - word);
(void) fputs(word, stdout);
p = word;
bufleft = sizeof(word) - 1;
}
/*
* Print non-alphabetic characters as they are, without buffering:
*/
(void) putchar(c);
}
/*
* Handle any word still in buffer at the end of input:
*/
if (p != word) {
*p = '\0';
scramble(word, p - word);
(void) fputs(word, stdout);
}
return EXIT_SUCCESS;
}
static void
scramble(char *const word, const unsigned int length)
{
assert(word != NULL);
if (length > 3) {
int i; /* Index in word buffer */
int swaps; /* Number of letters swapped */
int remaining; /* Letters remaining to scramble */
int retries; /* Retries remaining in case of failure */
for (retries = MAX_RETRIES,
swaps = 0; swaps == 0 && retries > 0; --retries) {
for (remaining = length - 2, i = 1; remaining > 1;
--remaining, ++i) {
if (remaining > MAX_DISTANCE)
swaps += swap(word, i, i + rand() % MAX_DISTANCE);
else
swaps += swap(word, i, i + rand() % remaining);
}
}
}
}
static int
swap(char *const buf, const unsigned int a, const unsigned int b)
{
char tmp;
assert(buf != NULL);
if (a == b || buf[a] == buf[b])
return 0;
tmp = buf[a];
buf[a] = buf[b];
buf[b] = tmp;
return 1;
}