Homework project for Coursera.org course
Programming and the Web for Beginners
www.bit.ly/HowToCreateRandomPuzzlePages
Please note: This is a page in process. It is not complete
(Suggestions are warmly welcomed)

How to Create Random Puzzles on a Web Page

Image and video hosting by TinyPic
Sample Puzzle randomized by QB64.net program

1. Find a puzzle or problem to imitate

2. Is it okay to use such a found puzzle on your page

3. Rewrite the Puzzle or Problem into your Own Words

4. Identify all information that can be randomized

5. Plan the program to create random puzzles

6. Create Javascript to create the puzzle itself and display it.

Let's follow that process with the puzzle from Ganesh's page:

1. Here is a puzzle I want to imitate

The average age of three boys is 15 years. If their ages are in the ratio 3:5:7, what is the age of the youngest boy?

2. This puzzle is probably in the public domain.

3. Rewrite the Puzzle or Problem into Your own Words

There are ____ people at a _________ meeting.
Except for the youngest and the oldest, they all love ______ puzzles.
Their average age is fifteen.
Their ages have the relative ratio: __________.
How old are the two members who will not solve this puzzle?

As you can see, all the blanks can contain random information. In addition, all sentences can have variations if desired.

4. What information can be randomized

  1. The number of the people at the event (3, 4, 5, 6, 7, 8, 9)
  2. The name of the group: (the 'Triple Nine Society'; Mensa; Intertel, the One in a Thousand Society, TOPS)
  3. The name of a restaurant or place you might eat lunch or dinner (Pizza Hut, Civic Park, Restaurant)
  4. Type of puzzle you like to solve (math, logic, word, algebra, geometry)
  5. Average age (to be calculated)
  6. Ratio of ages (to be calculated)
  7. The answers (to be calculated)

5. Plan the program to create random puzzles (flowchart, etc.)

I learned FORTRAN in 1969, and later learned COBOL, RPG and other languages, but I've been thinking in the old DOS-style BASIC since 1977. I've even dreamed about writing programs, even writing a four-dimensional maze program during a dream one night in the 1980's. (I got up, used that dreamt logic to create a 3D maze, and it worked in real life. Later, I did write the 4D version.) This paragraph is meant to help the reader realize that I took the Coursera course in order to learn Javascript. I already knew BASIC.

When I decided to use this for my Coursera project, I stole a puzzle from a friend of mine (with permission) and wrote a QB64.net program to randomly create a few puzzles, to see how it worked. This process will will help me when I get to the point of rewriting the program in Javascript, because I'll already know the logic works. P> The process I went through to write that program is similar to the process you would use to write a program in Javascript, so let's outline the process of writing a program to create the puzzle above. NOTE that my original list of tasks was shorter, but as I tested the program, I found errors I needed to look out for.

  1. Define the output object (screen in BASIC, image or ??? in Javascript?)
  2. Be sure the program is random
  3. Set up storage arrays for ages and ratios
  4. Pick a random Group Name
  5. Pick a random Restaurant or Place Name
  6. Pick a random Puzzle Type
  7. Pick a random number of people to attend the event
  8. Convert that number to a word for story-description
  9. Pick a random number to be GCF of all ages in the puzzle
  10. ERROR ALERT: Be sure # of people and random GCF do not share a common factor
  11. Generate all the ratios needed for the problem
  12. Multiply all ratios by the GCF to get all the ages for the problem
  13. Add up the ages and calculate the average age
  14. ERROR ALERT: Reject any set of ages where average is not an integer
  15. ERROR ALERT: Reject any set of ages with triplets, quads, etc.
  16. Convert ratio values into a ratio string for display
  17. Display the problem on screen or in window or inside image

Here is the updated QB64 program that includes all of the above:

'COMMENT: (For best readability, expand your page width so that these two comment lines (with all the dashes --------------)
'-------: -----------------: appear full width, the indenting and program comments will be much easier to read as a result.)
'
DEFINT A-S '                                    All variables that start with A thru S are integers
DEFSTR T-Z '                                    All variables that start with T thru Z are strings
SCREEN _NEWIMAGE(1920, 1080, 256) '             set SCREEN to half of maximum 2880x1630 resolution
RANDOMIZE TIMER '                               This uses seconds since midnight to randomize the random number generator
DIM age(9), ratio(9) '                          define arrays to hold random values
' Next 3 lines are the DATA that is used by FUNCTION RandomW$ to pick random info to include in program
zg = "TNS,Triple 9 Society,Intertel,OATHS,One in 1000 Society,TOPS,Top One % Society,Brain Society,Mensa,"
zp = "Math,Number,Arithmetic,Maths,Mathematical,Arithmetical,Algebra,Algebraical,Numeric,"
zr = "Pizza Hut,Subway,Dominos,Wendys,McDonalds,Sonic,Shoneys,Dennys,Central Park,Burger King,Hardees,"
group = 0 '                                     First "group" is random. Choose specific group number with 1-9 after answer
DO '                                            DO all code until we press ESCape after seeing the answers
    bc = INT(RND * 3) * 8 + 24 '                    Random function returns 24, 32 (blue) or 40 (red) background color
    IF bc < 30 THEN bc = 9 '                        Change 24 to 9 (blue) background color (all reasonable dark choices)
    fc = INT(RND * 6) + 10 '                        Random foreground color (all reasonable bright choices)
    COLOR fc, bc '                                  set foreground and background colors as randomly generated
    zGroup = RandomW$(zg, group) '                  Pick a random group name (might not be in every puzzle generated)
    zPlace = RandomW$(zr, 0) '                      Pick a random eating place (might not be in every puzzle generated)
    zPtype = RandomW$(zp, 0) '                      Pick a random type of puzzle
    CLS '                                           Clear Screen, entire screen white
    DO '                                            DO all following statements WHILE final statements are true
        REDIM k(999) '                              reDIM array that checks for too many of one age
        n = INT(RND * 7) + 3 '                      How many people in the group
        zCount = CountWord$(n) '                    Get word to use for number of people in puzzle
        DO: gcf = INT(RND * 4) + 2 '                Greatest Common Factor of their ages
        LOOP WHILE GCFactor(n, gcf) > 1 '           Do not allow the number of people and the GCF to share a common factor
        min = INT(RND * 9 + 5) '                    Minimum age in group
        sum = 0 '                                   sum for calculating average later
        FOR a = 1 TO n - 1 '                        generate ages for all except one person
            g = INT(min / gcf) * gcf + INT(RND * 9) '   start with the ratio of their ages
            ratio(a) = g '                              and store that value as an individual ratio
            g = g * gcf: age(a) = g '                   then multiply by the GCF to get the person's age
            sum = sum + age(a): 'PRINT age(a); '        add up the ages (PRINT was used during debugging to see all tries)
        NEXT '                                      do this for all 'N' people
        g = (sum + n) MOD n '                       this is minimum possible age for last person in room
        WHILE g < min OR g MOD gcf '                WHILE that age is invalid
            g = g + n '                                 keep adding 'N' to that age until that age is divisible by 'GCF'
        WEND '                                      WEND: This is why we did not let 'N' and 'GCF' have a common factor
        age(n) = g: ratio(n) = g \ gcf '            store that last age and ratio into their arrays
        sum = sum + g: avg = sum / n '              and calculate the average age
        'PRINT age(n); ":"; sum; avg; gcf '         (PRINT used during debugging to see calculated last age, sum, etc.
        z = "": EC = 0 '                            Set up string to remember the relative ratios, and check for errors
        FOR i = 1 TO n '                            FOR every person in the group
            FOR j = i + 1 TO n '                        FOR every other other person in the group
                IF ratio(i) > ratio(j) THEN '               IF their ages are out of order
                    SWAP ratio(i), ratio(j) '                   SWAP the ratios
                    SWAP age(i), age(j) '                       SWAP the ages
                END IF '                                    end of IF block
            NEXT '                                      NEXT - at end of this loop, person 'I' has the correct age values
            g = age(i) '                                this statement saves a few bytes of memory
            k(g) = k(g) + 1 '                           Count how many of each age is in the sorted array
            IF k(g) > 2 THEN EC = 1 '                   If there are more than 2 people of one age, generate an error code
            z = z + ":" + LTRIM$(STR$(ratio(i))) '      This string contains all the ratio values for later display
        NEXT '                                      NEXT 'this loop finishes work for sorting ages into order
    LOOP WHILE age(n) > 99 OR sum MOD n OR EC ' LOOP until no centennarians, average is valid, & there are no errors

    '                                           PROBLEM || Display puzzle including zGroup (group name), zPtype (Puz Type),
    PRINT '                                     PUZZLE  || zPlace (restaurant/place), zCount (# of people), z (ratio) with
    '                                           PRINT   || three different types of situation (meeting, lunch, dinner)
    '                                           SECTION || and three different questions that mean the same thing; and
    '                                                   || give credit to the Coursera Homework development page:
    ptb = 40
    tb = " º" + SPACE$(ptb - 3) + "º"
    PRINT " É"; STRING$(ptb - 3, 205); "»"
    '
    'Mathematical,
    'Central Park,

    IF RND < .334 THEN
        PRINT " º There are "; zCount; " people at a"; TAB(ptb); "º" '           There are three people at a
        PRINT " º "; zGroup; " meeting."; TAB(ptb); "º" '                        One in 1000 Society meeting.
    ELSE
        IF RND < .5 AND LEN(zGroup) < 12 AND LEN(zPlace) < 9 THEN
            PRINT " º "; Cap$(zCount); " "; zGroup; " members"; TAB(ptb); "º" '  Three One in 1000 Society members
            PRINT " º met at "; zPlace; " for lunch."; TAB(ptb); "º" '           met at Central Park for lunch.
        ELSE
            PRINT " º "; Cap$(zCount); " friends met for lunch"; TAB(ptb); "º" ' Three friends met for lunch
            PRINT " º at "; zPlace; " today."; TAB(ptb); "º" '                   at Central Park today.
        END IF
    END IF
    PRINT tb '                                                                º 12345678901234567890123456789012345 º
    IF RND < .5 THEN
        PRINT " º Except for the youngest and oldest,"; TAB(ptb); "º" '          Except for the youngest and oldest
        PRINT " º they all love "; zPtype; " puzzles."; TAB(ptb); "º" '          they all love mathematical puzzles.
    ELSE
        PRINT " º The youngest and oldest members"; TAB(ptb); "º" '              The youngest and oldest members
        PRINT " º do not enjoy "; zPtype; " puzzles."; TAB(ptb); "º" '           do not enjoy mathematical puzzles.
    END IF
    PRINT tb
    PRINT " º Their average age is "; CountWord$(avg); "."; TAB(ptb); "º" '       Their average age is seventy-seven.
    PRINT tb
    IF RND < .5 THEN
        PRINT " º Their ages have the relative ratio: º" '                       Their ages have the relative ratio:
    ELSE
        PRINT " º The ratio between their ages is:    º" '                       The ratio between their ages is:
        PRINT " º "; z; TAB(ptb); "º"
    END IF
    PRINT tb
    SELECT CASE INT(RND * 3)
        CASE 0
            PRINT " º How old are the two people who will º"
            PRINT " º just ask everyone their ages?       º"
        CASE 1
            PRINT " º What are the ages of the members    º"
            PRINT " º who won't solve this puzzle?        º"
        CASE 2
            PRINT " º How old are the two people who are  º"
            PRINT " º so woefully puzzle-challenged?      º"
    END SELECT
    PRINT tb
    '      º 12345678901234567890123456789012345 º
    PRINT " º bit.ly/HowToCreateRandomPuzzlePages º"
    PRINT " ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ"
    PRINT INPUT$(1) '                                                                       Pause after displaying puzzle
    '
    PRINT " Youngest:"; age(1); " Oldest:"; age(n) ' Display answers
    PRINT
    FOR i = 1 TO n: PRINT STR$(age(i));: NEXT '      Show all of the ages
    PRINT ": Sum"; sum; " GCF"; gcf '                Print sum and Greatest Common Factor
    z = INPUT$(1) '                                  Pause until (1-9 next group), (ESCape) or (any other key random group)
    group = VAL(z) '                                 If key pressed is 1-9, next puzzle will include group # that number
LOOP UNTIL z = CHR$(27) '                       LOOP: keep generating new puzzles until ESC pressed twice
END '                                           END of main program

FUNCTION GCFactor (a, b)
FOR c = 1 TO a '                                        FOR every value of 'c' up to 'a'
    IF c * (a \ c) = a AND c * (b \ c) = b THEN d = c '     If A and B both divisible by C, then D is GCF of A and B
NEXT '                                                  NEXT value of c
GCFactor = d '                                          RETURN the value of the greatest common factor
END FUNCTION '                                          NOTE: If you initialize 'd=1' change FOR loop to '= 2 to a'

FUNCTION CountWord$ (a)
z = "one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir,four,fif,six,seven,eigh,nine,"
y = "twenty ,thirty ,forty  ,fifty  ,sixty  ,seventy,eighty ,ninety"
w = "" '                                            W, the tens word, starts out blank
IF a > 19 THEN '                                    If a ten word is required for 20-something, etc.
    w = RTRIM$(MID$(y, INT(a / 10) * 8 - 15, 7)) '      W now contains the ten word
    a = a MOD 10 '                                      A now contains only the unit digit
    IF a THEN w = w + "-" '                             If A is not zero, add a dash to the tens word
END IF '                                            End of TEN word IF block --> 'W' now contains ten word
FOR i = 1 TO a '                                    FOR thru loop until we get to 'A' value (loop skipped if A=0)
    k = INSTR(z, ",") '                                 Look for comma inside 'Z' string
    x = LEFT$(z, k - 1) '                               X is equal to the word before the comma
    z = MID$(z, k + 1) '                                Remove that word from beginning of the 'Z' string
NEXT '                                              NEXT until we get to A --> X now contains unit word (or ten-teen word)
IF a > 12 THEN x = x + "teen" '                     This line saved 32 characters of data at cost of less length of code.s
CountWord$ = w + x '                                RETURN the combined word for the original 'A' value
END FUNCTION '                                      END of FUNCTION CALL

6. Create Javascript to create the puzzle itself and display it.